Merge remote branch 'goog/dalvik-dev' into mm
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..8f7e7ea
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,63 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bouncycastle
+LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_JAVA_LIBRARIES := core
+LOCAL_NO_STANDARD_LIBRARIES := true
+include $(BUILD_JAVA_LIBRARY)
+
+# Based on "Finding dead code" example in ProGuard manual at http://proguard.sourceforge.net/
+.PHONY: bouncycastle-proguard-deadcode
+bouncycastle-proguard-deadcode: $(full_classes_compiled_jar) $(full_java_libs)
+	$(PROGUARD) \
+		-injars $(full_classes_compiled_jar) \
+		-libraryjars "$(call normalize-path-list,$(addsuffix (!org/bouncycastle/**.class,!org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigest.class),$(full_java_libs)))" \
+		-dontoptimize \
+		-dontobfuscate \
+		-dontpreverify \
+		-ignorewarnings \
+		-printusage \
+		-keep class org.bouncycastle.jce.provider.BouncyCastleProvider "{ public protected *; }" \
+		-keep class org.bouncycastle.jce.provider.symmetric.AESMappings "{ public protected *; }" \
+		-keep class org.bouncycastle.asn1.ASN1TaggedObject "{ public protected *; }" \
+		-keep class org.bouncycastle.asn1.x509.CertificateList "{ public protected *; }" \
+		-keep class org.bouncycastle.crypto.AsymmetricBlockCipher "{ public protected *; }" \
+		-keep class org.bouncycastle.x509.ExtendedPKIXBuilderParameters "{ public protected *; }" \
+		`(find $(LOCAL_PATH) -name '*.java' | xargs grep '"org.bouncycastle' | egrep '  (put|add)' | sed -e 's/");//' -e 's/.*"//'; \
+		  find $(LOCAL_PATH) -name '*.java' | xargs grep '  addHMACAlgorithm' | sed 's/"org.bouncycastle/\norg.bouncycastle/g' | grep ^org.bouncycastle | sed 's/".*//'; \
+                  find . -name '*.java' | xargs grep 'import org.bouncycastle' | grep -v /bouncycastle/ | sed -e 's/.*:import //' -e 's/;//') \
+		  | sed -e 's/^/-keep class /' -e 's/$$/ { public protected \*; } /' | sort | uniq` \
+		-keepclassmembers "class * { \
+		    static final %                *; \
+		    static final java.lang.String *; \
+		}" \
+		-keepclassmembers "class * implements java.io.Serializable { \
+		    private static final java.io.ObjectStreamField[] serialPersistentFields; \
+		    private void writeObject(java.io.ObjectOutputStream); \
+		    private void readObject(java.io.ObjectInputStream); \
+		    java.lang.Object writeReplace(); \
+		    java.lang.Object readResolve(); \
+		}" \
+		-keepclassmembers "interface org.bouncycastle.crypto.paddings.BlockCipherPadding { \
+		    abstract public java.lang.String getPaddingName(); \
+		}" \
+		-keepclassmembers "class * implements org.bouncycastle.crypto.paddings.BlockCipherPadding { \
+		    public java.lang.String getPaddingName(); \
+		}"
diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD_LIKE
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..9c07f83
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,22 @@
+<html>
+<body bgcolor=#ffffff>
+
+Copyright (c) 2000-2009 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
+<p>
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
+and associated documentation files (the "Software"), to deal in the Software without restriction, 
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+<p>
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+<p>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+</body>
+</html>
diff --git a/README.android b/README.android
new file mode 100644
index 0000000..4e041ed
--- /dev/null
+++ b/README.android
@@ -0,0 +1,51 @@
+Bouncy Castle on the Android platform.
+---
+
+The code in this directory is based on $BOUNCYCASTLE_VERSION in the
+file bouncycastle.version. See patches/README for more information on
+how the code differs from $BOUNCYCASTLE_VERSION.
+
+Porting New Versions of Bouncy Castle.
+--
+
+The following steps are recommended for porting new Bouncy Castle versions.
+
+1) Retrieve the appropriate version of the Bouncy Castle source from
+   www.bouncycastle.org/latest_releases.html (in bcprov-jdk*-*.tar.gz
+   file). Check the checksum (found at bouncycastle.org/checksums.html) with:
+
+     md5sum bcprov-jdk*-*.tar.gz
+     sha1sum bcprov-jdk*-*.tar.gz
+
+2) Update the variables in bouncycastle.config and bouncycastle.version as appropriate.
+   At the very least you will need to update the bouncycastle.version.
+
+3) Run:
+
+     ./import_bouncycastle.sh import bcprov-jdk*-*.tar.gz
+
+4) If there are any errors, then modify bouncycastle.config, bouncycastle.version
+   and patches in patches/ as appropriate.  You might want to use:
+
+     ./import_bouncycastle.sh regenerate patches/*.patch
+
+   Repeat step 3.
+
+5) Cleanup before building with:
+
+     m -j16 clean-bouncycastle
+
+6) Build the bouncycastle target from the external/bouncycastle directory with:
+
+     mm -j16 snod && adb sync system
+
+   If there are build errors, then patches/*.mk or bouncycastle.config
+   may need updating.
+
+7) Run tests to make sure things are working:
+
+     See external/openssl/README.android for test instructions
+
+8) Do a full build before checking in:
+
+     m -j16
diff --git a/bouncycastle.config b/bouncycastle.config
new file mode 100644
index 0000000..738b468
--- /dev/null
+++ b/bouncycastle.config
@@ -0,0 +1,447 @@
+# directories
+UNNEEDED_SOURCES=" \
+org/bouncycastle/asn1/cmp \
+org/bouncycastle/asn1/cms/ecc \
+org/bouncycastle/asn1/crmf \
+org/bouncycastle/asn1/eac \
+org/bouncycastle/asn1/esf \
+org/bouncycastle/asn1/ess \
+org/bouncycastle/asn1/gnu \
+org/bouncycastle/asn1/icao \
+org/bouncycastle/asn1/kisa \
+org/bouncycastle/asn1/microsoft \
+org/bouncycastle/asn1/mozilla \
+org/bouncycastle/asn1/ntt \
+org/bouncycastle/asn1/ocsp \
+org/bouncycastle/asn1/sec \
+org/bouncycastle/asn1/smime \
+org/bouncycastle/asn1/test \
+org/bouncycastle/asn1/tsp \
+org/bouncycastle/asn1/x500 \
+org/bouncycastle/asn1/x509/qualified \
+org/bouncycastle/asn1/x509/sigi \
+org/bouncycastle/bcpg \
+org/bouncycastle/cms \
+org/bouncycastle/crypto/agreement/kdf \
+org/bouncycastle/crypto/agreement/srp \
+org/bouncycastle/crypto/examples \
+org/bouncycastle/crypto/prng \
+org/bouncycastle/crypto/test \
+org/bouncycastle/crypto/tls/ \
+org/bouncycastle/i18n/ \
+org/bouncycastle/jce/examples \
+org/bouncycastle/jce/provider/asymmetric/ \
+org/bouncycastle/jce/provider/test \
+org/bouncycastle/mail \
+org/bouncycastle/math \
+org/bouncycastle/mozilla \
+org/bouncycastle/ocsp \
+org/bouncycastle/openpgp \
+org/bouncycastle/openssl/test \
+org/bouncycastle/sasn1 \
+org/bouncycastle/tsp \
+org/bouncycastle/util/encoders/test \
+org/bouncycastle/util/test \
+org/bouncycastle/voms \
+org/bouncycastle/x509/examples \
+"
+
+# files
+UNNEEDED_SOURCES+=" \
+org/bouncycastle/LICENSE.java \
+org/bouncycastle/asn1/ASN1Generator.java \
+org/bouncycastle/asn1/BERGenerator.java \
+org/bouncycastle/asn1/BERNull.java \
+org/bouncycastle/asn1/BEROctetStringGenerator.java \
+org/bouncycastle/asn1/BERSequenceGenerator.java \
+org/bouncycastle/asn1/DERGenerator.java \
+org/bouncycastle/asn1/DERSequenceGenerator.java \
+org/bouncycastle/asn1/cms/Attribute.java \
+org/bouncycastle/asn1/cms/AttributeTable.java \
+org/bouncycastle/asn1/cms/AuthEnvelopedData.java \
+org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java \
+org/bouncycastle/asn1/cms/AuthenticatedData.java \
+org/bouncycastle/asn1/cms/AuthenticatedDataParser.java \
+org/bouncycastle/asn1/cms/CMSAttributes.java \
+org/bouncycastle/asn1/cms/CompressedData.java \
+org/bouncycastle/asn1/cms/CompressedDataParser.java \
+org/bouncycastle/asn1/cms/ContentInfoParser.java \
+org/bouncycastle/asn1/cms/EncryptedContentInfo.java \
+org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java \
+org/bouncycastle/asn1/cms/EncryptedData.java \
+org/bouncycastle/asn1/cms/EnvelopedData.java \
+org/bouncycastle/asn1/cms/EnvelopedDataParser.java \
+org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java \
+org/bouncycastle/asn1/cms/KEKIdentifier.java \
+org/bouncycastle/asn1/cms/KEKRecipientInfo.java \
+org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java \
+org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java \
+org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java \
+org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java \
+org/bouncycastle/asn1/cms/OriginatorInfo.java \
+org/bouncycastle/asn1/cms/OriginatorPublicKey.java \
+org/bouncycastle/asn1/cms/OtherKeyAttribute.java \
+org/bouncycastle/asn1/cms/OtherRecipientInfo.java \
+org/bouncycastle/asn1/cms/PasswordRecipientInfo.java \
+org/bouncycastle/asn1/cms/RecipientEncryptedKey.java \
+org/bouncycastle/asn1/cms/RecipientIdentifier.java \
+org/bouncycastle/asn1/cms/RecipientInfo.java \
+org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java \
+org/bouncycastle/asn1/cms/SignedData.java \
+org/bouncycastle/asn1/cms/SignedDataParser.java \
+org/bouncycastle/asn1/cms/SignerIdentifier.java \
+org/bouncycastle/asn1/cms/SignerInfo.java \
+org/bouncycastle/asn1/cms/Time.java \
+org/bouncycastle/asn1/cms/package.html \
+org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java \
+org/bouncycastle/asn1/cryptopro/ECGOST3410ParamSetParameters.java \
+org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java \
+org/bouncycastle/asn1/cryptopro/GOST3410NamedParameters.java \
+org/bouncycastle/asn1/cryptopro/GOST3410ParamSetParameters.java \
+org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java \
+org/bouncycastle/asn1/cryptopro/package.html \
+org/bouncycastle/asn1/isismtt/ocsp/CertHash.java \
+org/bouncycastle/asn1/isismtt/ocsp/RequestedCertificate.java \
+org/bouncycastle/asn1/isismtt/x509/AdditionalInformationSyntax.java \
+org/bouncycastle/asn1/isismtt/x509/AdmissionSyntax.java \
+org/bouncycastle/asn1/isismtt/x509/Admissions.java \
+org/bouncycastle/asn1/isismtt/x509/DeclarationOfMajority.java \
+org/bouncycastle/asn1/isismtt/x509/MonetaryLimit.java \
+org/bouncycastle/asn1/isismtt/x509/NamingAuthority.java \
+org/bouncycastle/asn1/isismtt/x509/ProcurationSyntax.java \
+org/bouncycastle/asn1/isismtt/x509/ProfessionInfo.java \
+org/bouncycastle/asn1/isismtt/x509/Restriction.java \
+org/bouncycastle/asn1/misc/CAST5CBCParameters.java \
+org/bouncycastle/asn1/misc/IDEACBCPar.java \
+org/bouncycastle/asn1/misc/package.html \
+org/bouncycastle/asn1/nist/NISTNamedCurves.java \
+org/bouncycastle/asn1/nist/package.html \
+org/bouncycastle/asn1/oiw/ElGamalParameter.java \
+org/bouncycastle/asn1/oiw/package.html \
+org/bouncycastle/asn1/package.html \
+org/bouncycastle/asn1/pkcs/Attribute.java \
+org/bouncycastle/asn1/pkcs/PBES2Algorithms.java \
+org/bouncycastle/asn1/pkcs/PBES2Parameters.java \
+org/bouncycastle/asn1/pkcs/PBKDF2Params.java \
+org/bouncycastle/asn1/pkcs/RC2CBCParameter.java \
+org/bouncycastle/asn1/pkcs/SignerInfo.java \
+org/bouncycastle/asn1/pkcs/package.html \
+org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java \
+org/bouncycastle/asn1/teletrust/package.html \
+org/bouncycastle/asn1/util/DERDump.java \
+org/bouncycastle/asn1/util/Dump.java \
+org/bouncycastle/asn1/util/package.html \
+org/bouncycastle/asn1/x509/AccessDescription.java \
+org/bouncycastle/asn1/x509/AuthorityInformationAccess.java \
+org/bouncycastle/asn1/x509/CertPolicyId.java \
+org/bouncycastle/asn1/x509/CertificatePair.java \
+org/bouncycastle/asn1/x509/CertificatePolicies.java \
+org/bouncycastle/asn1/x509/DisplayText.java \
+org/bouncycastle/asn1/x509/ExtendedKeyUsage.java \
+org/bouncycastle/asn1/x509/IetfAttrSyntax.java \
+org/bouncycastle/asn1/x509/KeyPurposeId.java \
+org/bouncycastle/asn1/x509/NoticeReference.java \
+org/bouncycastle/asn1/x509/PolicyMappings.java \
+org/bouncycastle/asn1/x509/PolicyQualifierId.java \
+org/bouncycastle/asn1/x509/PolicyQualifierInfo.java \
+org/bouncycastle/asn1/x509/PrivateKeyUsagePeriod.java \
+org/bouncycastle/asn1/x509/RoleSyntax.java \
+org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java \
+org/bouncycastle/asn1/x509/Target.java \
+org/bouncycastle/asn1/x509/TargetInformation.java \
+org/bouncycastle/asn1/x509/Targets.java \
+org/bouncycastle/asn1/x509/UserNotice.java \
+org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java \
+org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java \
+org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java \
+org/bouncycastle/asn1/x509/X509Attributes.java \
+org/bouncycastle/asn1/x509/package.html \
+org/bouncycastle/asn1/x9/KeySpecificInfo.java \
+org/bouncycastle/asn1/x9/OtherInfo.java \
+org/bouncycastle/asn1/x9/X962NamedCurves.java \
+org/bouncycastle/asn1/x9/X962Parameters.java \
+org/bouncycastle/asn1/x9/X9Curve.java \
+org/bouncycastle/asn1/x9/X9ECParameters.java \
+org/bouncycastle/asn1/x9/X9ECParametersHolder.java \
+org/bouncycastle/asn1/x9/X9ECPoint.java \
+org/bouncycastle/asn1/x9/X9FieldElement.java \
+org/bouncycastle/asn1/x9/X9FieldID.java \
+org/bouncycastle/asn1/x9/X9IntegerConverter.java \
+org/bouncycastle/asn1/x9/package.html \
+org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java \
+org/bouncycastle/crypto/MaxBytesExceededException.java \
+org/bouncycastle/crypto/StreamBlockCipher.java \
+org/bouncycastle/crypto/StreamCipher.java \
+org/bouncycastle/crypto/agreement/DHAgreement.java \
+org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java \
+org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java \
+org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java \
+org/bouncycastle/crypto/agreement/package.html \
+org/bouncycastle/crypto/digests/GOST3411Digest.java \
+org/bouncycastle/crypto/digests/MD2Digest.java \
+org/bouncycastle/crypto/digests/MD4Digest.java \
+org/bouncycastle/crypto/digests/RIPEMD128Digest.java \
+org/bouncycastle/crypto/digests/RIPEMD160Digest.java \
+org/bouncycastle/crypto/digests/RIPEMD256Digest.java \
+org/bouncycastle/crypto/digests/RIPEMD320Digest.java \
+org/bouncycastle/crypto/digests/SHA224Digest.java \
+org/bouncycastle/crypto/digests/ShortenedDigest.java \
+org/bouncycastle/crypto/digests/TigerDigest.java \
+org/bouncycastle/crypto/digests/WhirlpoolDigest.java \
+org/bouncycastle/crypto/digests/package.html \
+org/bouncycastle/crypto/encodings/package.html \
+org/bouncycastle/crypto/engines/AESLightEngine.java \
+org/bouncycastle/crypto/engines/BlowfishEngine.java \
+org/bouncycastle/crypto/engines/CAST5Engine.java \
+org/bouncycastle/crypto/engines/CAST6Engine.java \
+org/bouncycastle/crypto/engines/CamelliaEngine.java \
+org/bouncycastle/crypto/engines/CamelliaLightEngine.java \
+org/bouncycastle/crypto/engines/CamelliaWrapEngine.java \
+org/bouncycastle/crypto/engines/ElGamalEngine.java \
+org/bouncycastle/crypto/engines/GOST28147Engine.java \
+org/bouncycastle/crypto/engines/Grain128Engine.java \
+org/bouncycastle/crypto/engines/Grainv1Engine.java \
+org/bouncycastle/crypto/engines/HC128Engine.java \
+org/bouncycastle/crypto/engines/HC256Engine.java \
+org/bouncycastle/crypto/engines/IDEAEngine.java \
+org/bouncycastle/crypto/engines/IESEngine.java \
+org/bouncycastle/crypto/engines/ISAACEngine.java \
+org/bouncycastle/crypto/engines/NaccacheSternEngine.java \
+org/bouncycastle/crypto/engines/NoekeonEngine.java \
+org/bouncycastle/crypto/engines/NullEngine.java \
+org/bouncycastle/crypto/engines/RC2WrapEngine.java \
+org/bouncycastle/crypto/engines/RC4Engine.java \
+org/bouncycastle/crypto/engines/RC6Engine.java \
+org/bouncycastle/crypto/engines/RFC3211WrapEngine.java \
+org/bouncycastle/crypto/engines/RSABlindingEngine.java \
+org/bouncycastle/crypto/engines/RSAEngine.java \
+org/bouncycastle/crypto/engines/RijndaelEngine.java \
+org/bouncycastle/crypto/engines/SEEDEngine.java \
+org/bouncycastle/crypto/engines/SEEDWrapEngine.java \
+org/bouncycastle/crypto/engines/Salsa20Engine.java \
+org/bouncycastle/crypto/engines/SerpentEngine.java \
+org/bouncycastle/crypto/engines/SkipjackEngine.java \
+org/bouncycastle/crypto/engines/TEAEngine.java \
+org/bouncycastle/crypto/engines/TwofishEngine.java \
+org/bouncycastle/crypto/engines/VMPCEngine.java \
+org/bouncycastle/crypto/engines/VMPCKSA3Engine.java \
+org/bouncycastle/crypto/engines/XTEAEngine.java \
+org/bouncycastle/crypto/engines/package.html \
+org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java \
+org/bouncycastle/crypto/generators/DHKeyPairGenerator.java \
+org/bouncycastle/crypto/generators/ECKeyPairGenerator.java \
+org/bouncycastle/crypto/generators/ElGamalKeyPairGenerator.java \
+org/bouncycastle/crypto/generators/ElGamalParametersGenerator.java \
+org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java \
+org/bouncycastle/crypto/generators/GOST3410ParametersGenerator.java \
+org/bouncycastle/crypto/generators/KDF1BytesGenerator.java \
+org/bouncycastle/crypto/generators/KDF2BytesGenerator.java \
+org/bouncycastle/crypto/generators/MGF1BytesGenerator.java \
+org/bouncycastle/crypto/generators/NaccacheSternKeyPairGenerator.java \
+org/bouncycastle/crypto/generators/RSABlindingFactorGenerator.java \
+org/bouncycastle/crypto/generators/package.html \
+org/bouncycastle/crypto/io/SignerInputStream.java \
+org/bouncycastle/crypto/io/SignerOutputStream.java \
+org/bouncycastle/crypto/io/package.html \
+org/bouncycastle/crypto/macs/BlockCipherMac.java \
+org/bouncycastle/crypto/macs/CFBBlockCipherMac.java \
+org/bouncycastle/crypto/macs/GOST28147Mac.java \
+org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java \
+org/bouncycastle/crypto/macs/OldHMac.java \
+org/bouncycastle/crypto/macs/VMPCMac.java \
+org/bouncycastle/crypto/macs/package.html \
+org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.java \
+org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java \
+org/bouncycastle/crypto/modes/PaddedBlockCipher.java \
+org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java \
+org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java \
+org/bouncycastle/crypto/modes/package.html \
+org/bouncycastle/crypto/package.html \
+org/bouncycastle/crypto/paddings/package.html \
+org/bouncycastle/crypto/params/CCMParameters.java \
+org/bouncycastle/crypto/params/ECDomainParameters.java \
+org/bouncycastle/crypto/params/ECKeyGenerationParameters.java \
+org/bouncycastle/crypto/params/ECKeyParameters.java \
+org/bouncycastle/crypto/params/ECPrivateKeyParameters.java \
+org/bouncycastle/crypto/params/ECPublicKeyParameters.java \
+org/bouncycastle/crypto/params/ElGamalKeyGenerationParameters.java \
+org/bouncycastle/crypto/params/ElGamalKeyParameters.java \
+org/bouncycastle/crypto/params/ElGamalParameters.java \
+org/bouncycastle/crypto/params/ElGamalPrivateKeyParameters.java \
+org/bouncycastle/crypto/params/ElGamalPublicKeyParameters.java \
+org/bouncycastle/crypto/params/GOST3410KeyGenerationParameters.java \
+org/bouncycastle/crypto/params/GOST3410KeyParameters.java \
+org/bouncycastle/crypto/params/GOST3410Parameters.java \
+org/bouncycastle/crypto/params/GOST3410PrivateKeyParameters.java \
+org/bouncycastle/crypto/params/GOST3410PublicKeyParameters.java \
+org/bouncycastle/crypto/params/GOST3410ValidationParameters.java \
+org/bouncycastle/crypto/params/IESParameters.java \
+org/bouncycastle/crypto/params/IESWithCipherParameters.java \
+org/bouncycastle/crypto/params/ISO18033KDFParameters.java \
+org/bouncycastle/crypto/params/KDFParameters.java \
+org/bouncycastle/crypto/params/MGFParameters.java \
+org/bouncycastle/crypto/params/MQVPrivateParameters.java \
+org/bouncycastle/crypto/params/MQVPublicParameters.java \
+org/bouncycastle/crypto/params/NaccacheSternKeyGenerationParameters.java \
+org/bouncycastle/crypto/params/NaccacheSternKeyParameters.java \
+org/bouncycastle/crypto/params/NaccacheSternPrivateKeyParameters.java \
+org/bouncycastle/crypto/params/ParametersWithSBox.java \
+org/bouncycastle/crypto/params/ParametersWithSalt.java \
+org/bouncycastle/crypto/params/RSABlindingParameters.java \
+org/bouncycastle/crypto/params/package.html \
+org/bouncycastle/crypto/signers/DSADigestSigner.java \
+org/bouncycastle/crypto/signers/ECDSASigner.java \
+org/bouncycastle/crypto/signers/ECGOST3410Signer.java \
+org/bouncycastle/crypto/signers/ECNRSigner.java \
+org/bouncycastle/crypto/signers/GOST3410Signer.java \
+org/bouncycastle/crypto/signers/GenericSigner.java \
+org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java \
+org/bouncycastle/crypto/signers/ISO9796d2Signer.java \
+org/bouncycastle/crypto/signers/PSSSigner.java \
+org/bouncycastle/crypto/signers/package.html \
+org/bouncycastle/crypto/util/package.html \
+org/bouncycastle/jce/ECGOST3410NamedCurveTable.java \
+org/bouncycastle/jce/ECKeyUtil.java \
+org/bouncycastle/jce/ECNamedCurveTable.java \
+org/bouncycastle/jce/ECPointUtil.java \
+org/bouncycastle/jce/MultiCertStoreParameters.java \
+org/bouncycastle/jce/PKCS7SignedData.java \
+org/bouncycastle/jce/X509KeyUsage.java \
+org/bouncycastle/jce/X509LDAPCertStoreParameters.java \
+org/bouncycastle/jce/X509V1CertificateGenerator.java \
+org/bouncycastle/jce/X509V2CRLGenerator.java \
+org/bouncycastle/jce/X509V3CertificateGenerator.java \
+org/bouncycastle/jce/exception/ExtCertificateEncodingException.java \
+org/bouncycastle/jce/exception/ExtIOException.java \
+org/bouncycastle/jce/interfaces/ECKey.java \
+org/bouncycastle/jce/interfaces/ECPointEncoder.java \
+org/bouncycastle/jce/interfaces/ECPrivateKey.java \
+org/bouncycastle/jce/interfaces/ECPublicKey.java \
+org/bouncycastle/jce/interfaces/ElGamalKey.java \
+org/bouncycastle/jce/interfaces/ElGamalPrivateKey.java \
+org/bouncycastle/jce/interfaces/ElGamalPublicKey.java \
+org/bouncycastle/jce/interfaces/GOST3410Key.java \
+org/bouncycastle/jce/interfaces/GOST3410Params.java \
+org/bouncycastle/jce/interfaces/GOST3410PrivateKey.java \
+org/bouncycastle/jce/interfaces/GOST3410PublicKey.java \
+org/bouncycastle/jce/interfaces/IESKey.java \
+org/bouncycastle/jce/interfaces/MQVPrivateKey.java \
+org/bouncycastle/jce/interfaces/MQVPublicKey.java \
+org/bouncycastle/jce/interfaces/package.html \
+org/bouncycastle/jce/package.html \
+org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java \
+org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java \
+org/bouncycastle/jce/provider/BrokenPBE.java \
+org/bouncycastle/jce/provider/DSABase.java \
+org/bouncycastle/jce/provider/DSAEncoder.java \
+org/bouncycastle/jce/provider/ElGamalUtil.java \
+org/bouncycastle/jce/provider/GOST3410Util.java \
+org/bouncycastle/jce/provider/JCEECPrivateKey.java \
+org/bouncycastle/jce/provider/JCEECPublicKey.java \
+org/bouncycastle/jce/provider/JCEElGamalCipher.java \
+org/bouncycastle/jce/provider/JCEElGamalPrivateKey.java \
+org/bouncycastle/jce/provider/JCEElGamalPublicKey.java \
+org/bouncycastle/jce/provider/JCEIESCipher.java \
+org/bouncycastle/jce/provider/JCEStreamCipher.java \
+org/bouncycastle/jce/provider/JDKECDSAAlgParameters.java \
+org/bouncycastle/jce/provider/JDKGOST3410PrivateKey.java \
+org/bouncycastle/jce/provider/JDKGOST3410PublicKey.java \
+org/bouncycastle/jce/provider/JDKGOST3410Signer.java \
+org/bouncycastle/jce/provider/JDKISOSignature.java \
+org/bouncycastle/jce/provider/JDKPSSSigner.java \
+org/bouncycastle/jce/provider/MultiCertStoreSpi.java \
+org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java \
+org/bouncycastle/jce/provider/PKIXAttrCertPathValidatorSpi.java \
+org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java \
+org/bouncycastle/jce/provider/X509AttrCertParser.java \
+org/bouncycastle/jce/provider/X509CRLParser.java \
+org/bouncycastle/jce/provider/X509CertPairParser.java \
+org/bouncycastle/jce/provider/X509CertParser.java \
+org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java \
+org/bouncycastle/jce/provider/X509StoreAttrCertCollection.java \
+org/bouncycastle/jce/provider/X509StoreCRLCollection.java \
+org/bouncycastle/jce/provider/X509StoreCertCollection.java \
+org/bouncycastle/jce/provider/X509StoreCertPairCollection.java \
+org/bouncycastle/jce/provider/X509StoreLDAPAttrCerts.java \
+org/bouncycastle/jce/provider/X509StoreLDAPCRLs.java \
+org/bouncycastle/jce/provider/X509StoreLDAPCertPairs.java \
+org/bouncycastle/jce/provider/X509StoreLDAPCerts.java \
+org/bouncycastle/jce/provider/symmetric/CAST5.java \
+org/bouncycastle/jce/provider/symmetric/CAST5Mappings.java \
+org/bouncycastle/jce/provider/symmetric/Camellia.java \
+org/bouncycastle/jce/provider/symmetric/CamelliaMappings.java \
+org/bouncycastle/jce/provider/symmetric/Grain128.java \
+org/bouncycastle/jce/provider/symmetric/Grain128Mappings.java \
+org/bouncycastle/jce/provider/symmetric/Grainv1.java \
+org/bouncycastle/jce/provider/symmetric/Grainv1Mappings.java \
+org/bouncycastle/jce/provider/symmetric/IDEA.java \
+org/bouncycastle/jce/provider/symmetric/IDEAMappings.java \
+org/bouncycastle/jce/provider/symmetric/Noekeon.java \
+org/bouncycastle/jce/provider/symmetric/NoekeonMappings.java \
+org/bouncycastle/jce/provider/symmetric/SEED.java \
+org/bouncycastle/jce/provider/symmetric/SEEDMappings.java \
+org/bouncycastle/jce/spec/ECKeySpec.java \
+org/bouncycastle/jce/spec/ECNamedCurveParameterSpec.java \
+org/bouncycastle/jce/spec/ECNamedCurveSpec.java \
+org/bouncycastle/jce/spec/ECParameterSpec.java \
+org/bouncycastle/jce/spec/ECPrivateKeySpec.java \
+org/bouncycastle/jce/spec/ECPublicKeySpec.java \
+org/bouncycastle/jce/spec/ElGamalGenParameterSpec.java \
+org/bouncycastle/jce/spec/ElGamalKeySpec.java \
+org/bouncycastle/jce/spec/ElGamalParameterSpec.java \
+org/bouncycastle/jce/spec/ElGamalPrivateKeySpec.java \
+org/bouncycastle/jce/spec/ElGamalPublicKeySpec.java \
+org/bouncycastle/jce/spec/GOST28147ParameterSpec.java \
+org/bouncycastle/jce/spec/GOST3410ParameterSpec.java \
+org/bouncycastle/jce/spec/GOST3410PrivateKeySpec.java \
+org/bouncycastle/jce/spec/GOST3410PublicKeyParameterSetSpec.java \
+org/bouncycastle/jce/spec/GOST3410PublicKeySpec.java \
+org/bouncycastle/jce/spec/IEKeySpec.java \
+org/bouncycastle/jce/spec/IESParameterSpec.java \
+org/bouncycastle/jce/spec/MQVPrivateKeySpec.java \
+org/bouncycastle/jce/spec/MQVPublicKeySpec.java \
+org/bouncycastle/jce/spec/package.html \
+org/bouncycastle/openssl/PEMException.java \
+org/bouncycastle/openssl/PEMReader.java \
+org/bouncycastle/openssl/PasswordException.java \
+org/bouncycastle/openssl/PasswordFinder.java \
+org/bouncycastle/openssl/package.html \
+org/bouncycastle/util/AllTests.java \
+org/bouncycastle/util/CollectionStore.java \
+org/bouncycastle/util/IPTest.java \
+org/bouncycastle/util/StreamParser.java \
+org/bouncycastle/util/StreamParsingException.java \
+org/bouncycastle/util/encoders/BufferedDecoder.java \
+org/bouncycastle/util/encoders/BufferedEncoder.java \
+org/bouncycastle/util/encoders/HexTranslator.java \
+org/bouncycastle/util/encoders/Translator.java \
+org/bouncycastle/util/encoders/UrlBase64.java \
+org/bouncycastle/util/encoders/UrlBase64Encoder.java \
+org/bouncycastle/util/encoders/package.html \
+org/bouncycastle/x509/CertPathReviewerException.java \
+org/bouncycastle/x509/CertPathReviewerMessages_de.properties \
+org/bouncycastle/x509/NoSuchParserException.java \
+org/bouncycastle/x509/PKIXCertPathReviewer.java \
+org/bouncycastle/x509/X509AttributeCertStoreSelector.java \
+org/bouncycastle/x509/X509CertPairStoreSelector.java \
+org/bouncycastle/x509/X509CertificatePair.java \
+org/bouncycastle/x509/X509CollectionStoreParameters.java \
+org/bouncycastle/x509/X509StreamParser.java \
+org/bouncycastle/x509/X509StreamParserSpi.java \
+org/bouncycastle/x509/X509V1CertificateGenerator.java \
+org/bouncycastle/x509/X509V2AttributeCertificateGenerator.java \
+org/bouncycastle/x509/X509V2CRLGenerator.java \
+org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java \
+org/bouncycastle/x509/extension/SubjectKeyIdentifierStructure.java \
+org/bouncycastle/x509/extension/package.html \
+org/bouncycastle/x509/package.html \
+org/bouncycastle/x509/util/LDAPStoreHelper.java \
+org/bouncycastle/x509/util/StreamParser.java \
+org/bouncycastle/x509/util/StreamParsingException.java \
+"
+
+# needed sources to copy in
+NEEDED_SOURCES="org"
+
+# list of patch files to apply in the given order
+BOUNCYCASTLE_PATCHES="android.patch"
diff --git a/bouncycastle.version b/bouncycastle.version
new file mode 100644
index 0000000..4f5851f
--- /dev/null
+++ b/bouncycastle.version
@@ -0,0 +1,2 @@
+BOUNCYCASTLE_JDK=16
+BOUNCYCASTLE_VERSION=145
diff --git a/import_bouncycastle.sh b/import_bouncycastle.sh
new file mode 100755
index 0000000..2271dc0
--- /dev/null
+++ b/import_bouncycastle.sh
@@ -0,0 +1,242 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# This script imports new versions of Bouncy Castle (http://bouncycastle.org) into the
+# Android source tree.  To run, (1) fetch the appropriate tarball from the Bouncy Castle repository,
+# (2) check the checksum, and then (3) run:
+#   ./import_bouncycastle.sh bcprov-jdk*-*.tar.gz
+#
+# IMPORTANT: See README.android for additional details.
+
+# turn on exit on error as well as a warning when it happens
+set -e
+trap  "echo WARNING: Exiting on non-zero subprocess exit code" ERR;
+
+function die() {
+  declare -r message=$1
+
+  echo $message
+  exit 1
+}
+
+function usage() {
+  declare -r message=$1
+
+  if [ ! "$message" = "" ]; then
+    echo $message
+  fi
+  echo "Usage:"
+  echo "  ./import_bouncycastle.sh import </path/to/bcprov-jdk*-*.tar.gz>"
+  echo "  ./import_bouncycastle.sh regenerate <patch/*.patch>"
+  echo "  ./import_bouncycastle.sh generate <patch/*.patch> </path/to/bcprov-jdk*-*.tar.gz>"
+  exit 1
+}
+
+function main() {
+  if [ ! -d patches ]; then
+    die "Bouncy Castle patch directory patches/ not found"
+  fi
+
+  if [ ! -f bouncycastle.version ]; then
+    die "bouncycastle.version not found"
+  fi
+
+  source bouncycastle.version
+  if [ "$BOUNCYCASTLE_JDK" == "" -o "$BOUNCYCASTLE_VERSION" == "" ]; then
+    die "Invalid bouncycastle.version; see README.android for more information"
+  fi
+
+  BOUNCYCASTLE_DIR=bcprov-jdk$BOUNCYCASTLE_JDK-$BOUNCYCASTLE_VERSION
+  BOUNCYCASTLE_DIR_ORIG=$BOUNCYCASTLE_DIR.orig
+
+  if [ ! -f bouncycastle.config ]; then
+    die "bouncycastle.config not found"
+  fi
+
+  source bouncycastle.config
+  if [ "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then
+    die "Invalid bouncycastle.config; see README.android for more information"
+  fi
+
+  declare -r command=$1
+  shift || usage "No command specified. Try import, regenerate, or generate."
+  if [ "$command" = "import" ]; then
+    declare -r tar=$1
+    shift || usage "No tar file specified."
+    import $tar
+  elif [ "$command" = "regenerate" ]; then
+    declare -r patch=$1
+    shift || usage "No patch file specified."
+    [ -d $BOUNCYCASTLE_DIR ] || usage "$BOUNCYCASTLE_DIR not found, did you mean to use generate?"
+    [ -d $BOUNCYCASTLE_DIR_ORIG ] || usage "$BOUNCYCASTLE_DIR_ORIG not found, did you mean to use generate?"
+    regenerate $patch
+  elif [ "$command" = "generate" ]; then
+    declare -r patch=$1
+    shift || usage "No patch file specified."
+    declare -r tar=$1
+    shift || usage "No tar file specified."
+    generate $patch $tar
+  else
+    usage "Unknown command specified $command. Try import, regenerate, or generate."
+  fi
+}
+
+function import() {
+  declare -r BOUNCYCASTLE_SOURCE=$1
+
+  untar $BOUNCYCASTLE_SOURCE
+  applypatches
+
+  cd $BOUNCYCASTLE_DIR
+
+  cp -f LICENSE.html ../NOTICE
+  touch ../MODULE_LICENSE_BSD_LIKE
+
+  cd ..
+
+  rm -r src
+  mkdir -p src/main/java/
+  for i in $NEEDED_SOURCES; do
+    echo "Updating $i"
+    mv $BOUNCYCASTLE_DIR/$i src/main/java/
+  done
+
+  # if [ $BOUNCYCASTLE_VERSION -ge 145 ]; then
+  #   # move test directories from src/main/java to src/test/java
+  #   for from in `find src/main/java -name test`; do
+  #     to=`dirname $from | sed s,src/main/java/,src/test/java/,`
+  #     echo "Moving $from to $to"
+  #     mkdir -p $to
+  #     mv $from $to
+  #   done
+  # fi
+
+  # # move stray test files from src/main/java to src/test/java
+  # if [ $BOUNCYCASTLE_VERSION -ge 137 ]; then
+  #   mkdir -p src/test/java/org/bouncycastle/util/
+  #   echo "Moving src/main/java/org/bouncycastle/util tests"
+  #   mv src/main/java/org/bouncycastle/util/*Test*.java src/test/java/org/bouncycastle/util/
+  # fi
+
+  cleantar
+}
+
+function regenerate() {
+  declare -r patch=$1
+
+  generatepatch $patch
+}
+
+function generate() {
+  declare -r patch=$1
+  declare -r BOUNCYCASTLE_SOURCE=$2
+
+  untar $BOUNCYCASTLE_SOURCE
+  applypatches
+
+  # # restore stray test files from src/test/java back to src/main/java
+  # if [ $BOUNCYCASTLE_VERSION -ge 137 ]; then
+  #   echo "Restoring src/test/java/org/bouncycastle/util"
+  #   mv src/test/java/org/bouncycastle/util/* src/main/java/org/bouncycastle/util/
+  # fi
+
+  # # restore test directories from src/test/java back to src/main/java
+  # if [ $BOUNCYCASTLE_VERSION -ge 145 ]; then
+  #   for from in `find src/test/java -name test`; do
+  #     to=`dirname $from | sed s,src/test/java/,src/main/java/,`
+  #     echo "Restoring $from to $to"
+  #     mkdir -p $to
+  #     mv $from $to
+  #   done
+  # fi
+
+  for i in $NEEDED_SOURCES; do
+    echo "Restoring $i"
+    rm -r $BOUNCYCASTLE_DIR/$i
+    cp -rf src/main/java/$i $BOUNCYCASTLE_DIR/$i
+  done
+
+  generatepatch $patch
+  cleantar
+}
+
+function untar() {
+  declare -r BOUNCYCASTLE_SOURCE=$1
+
+  # Remove old source
+  cleantar
+
+  # Process new source
+  tar -zxf $BOUNCYCASTLE_SOURCE
+  mv $BOUNCYCASTLE_DIR $BOUNCYCASTLE_DIR_ORIG
+  find $BOUNCYCASTLE_DIR_ORIG -type f -print0 | xargs -0 chmod a-w
+  (cd $BOUNCYCASTLE_DIR_ORIG && unzip -q src.zip)
+  tar -zxf $BOUNCYCASTLE_SOURCE
+  (cd $BOUNCYCASTLE_DIR && unzip -q src.zip)
+
+  # Prune unnecessary sources
+  echo "Removing $UNNEEDED_SOURCES"
+  (cd $BOUNCYCASTLE_DIR_ORIG && rm -rf $UNNEEDED_SOURCES)
+  (cd $BOUNCYCASTLE_DIR      && rm -r  $UNNEEDED_SOURCES)
+}
+
+function cleantar() {
+  rm -rf $BOUNCYCASTLE_DIR_ORIG
+  rm -rf $BOUNCYCASTLE_DIR
+}
+
+function applypatches () {
+  cd $BOUNCYCASTLE_DIR
+
+  # Apply appropriate patches
+  for i in $BOUNCYCASTLE_PATCHES; do
+    echo "Applying patch $i"
+    patch -p1 < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
+
+    # make sure no UNNEEDED_SOURCES got into the patch
+    problem=0
+    for s in $UNNEEDED_SOURCES; do
+      if [ -e $s ]; then
+        echo Unneeded source $s restored by patch $i
+        problem=1
+      fi
+    done
+    if [ $problem = 1 ]; then
+      exit 1
+    fi
+  done
+
+  # Cleanup patch output
+  find . -type f -name "*.orig" -print0 | xargs -0 rm -f
+
+  cd ..
+}
+
+function generatepatch() {
+  declare -r patch=$1
+
+  # Cleanup stray files before generating patch
+  find $BOUNCYCASTLE_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f
+  find $BOUNCYCASTLE_DIR -type f -name "*~" -print0 | xargs -0 rm -f
+
+  rm -f $patch
+  LC_ALL=C TZ=UTC0 diff -Naur $BOUNCYCASTLE_DIR_ORIG $BOUNCYCASTLE_DIR >> $patch && die "ERROR: No diff for patch $path in file $i"
+  echo "Generated patch $patch"
+}
+
+main $@
diff --git a/patches/README b/patches/README
new file mode 100644
index 0000000..c6e4e6d
--- /dev/null
+++ b/patches/README
@@ -0,0 +1,31 @@
+android.patch:
+
+patch against Bouncy Castle:
+
+The main differences involve removing algorithms not included in the
+reference implementation (RI).  The libcore
+java.security.StandardNames test support class provides the most
+up-do-date documentation of differences between the RI's list of
+supported algorithms and Android's. Some notable omissions versus the
+RI:
+- ARCFOUR
+- Blowfish
+- LDAP
+- MD2
+- RC2
+
+Other performance (both speed and memory) changes:
+- singleton DERNull (BouncyCastle now does this but we make constructor private to be sure)
+- similarly made DERBoolean constructor private and moved to DERBoolean.{getInstance,TRUE,FALSE}
+- DERPrintableString interns its internal String values
+- DERObjectIdentifier interns its internal String indentifer value
+- changed uses of 'new Integer' to 'Integer.valueOf'
+- Added X509NameElementList to reduce small Vector allocation for X509Name key/value operations
+- Replaced X509Extensions hash/vector with new OrderedTable instance to cut down on memory allocation
+- PKCS12BagAttributeCarrier also uses OrderedTable to cut down on memory allocation
+- X509CertificateObject.getEncoded caches its result
+- Added IndexedPKIXParameters for faster cert lookup in CertPathValidatorUtilities.findTrustAnchor
+- CertPathValidatorUtilities.findTrustAnchor fast path compares encoded certs similar to PKIXCertPathValidatorSpi
+- Added ASN1Collection for use as new parent for ASN1Collection and ASN1Set to reduce small Vector allocation
+- removed references to SecretKeyFactory.PBE/PKCS5 SecretKeyFactory.PBE/PKCS12
+- OpenSSLDigest uses NativeCrypto JNI API
diff --git a/patches/android.patch b/patches/android.patch
new file mode 100644
index 0000000..8bb537c
--- /dev/null
+++ b/patches/android.patch
@@ -0,0 +1,13201 @@
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Collection.java bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Collection.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Collection.java	1970-01-01 00:00:00.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Collection.java	2010-06-25 23:17:26.000000000 +0000
+@@ -0,0 +1,298 @@
++package org.bouncycastle.asn1;
++
++import java.io.ByteArrayOutputStream;
++import java.io.IOException;
++import java.util.Enumeration;
++import java.util.ConcurrentModificationException;
++
++// BEGIN android-note
++/*
++ * This is a new class that was synthesized from ASN1Sequence and
++ * ASN1Set, but with extra smarts about efficiently storing its
++ * elements.
++ */
++// END android-note
++
++/**
++ * Base class for collection-like <code>DERObject</code>s. Instances
++ * of this class will keep up to four elements directly, resorting to
++ * an external collection only if more elements than that need to be
++ * stored.
++ */
++public abstract class ASN1Collection
++    extends ASN1Object
++{
++    /** &gt;= 0; size of the collection */
++    private int size;
++
++    /** null-ok; element #0 */
++    private DEREncodable obj0;
++
++    /** null-ok; element #1 */
++    private DEREncodable obj1;
++
++    /** null-ok; element #2 */
++    private DEREncodable obj2;
++
++    /** null-ok; element #3 */
++    private DEREncodable obj3;
++
++    /** null-ok; elements #4 and higher */
++    private DEREncodable[] rest;
++
++    /**
++     * Returns the object at the postion indicated by index.
++     *
++     * @param index the index (starting at zero) of the object
++     * @return the object at the postion indicated by index
++     */
++    public DEREncodable getObjectAt(int index) {
++        if ((index < 0) || (index >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(index));
++        }
++                    
++        switch (index) {
++            case 0: return obj0;
++            case 1: return obj1;
++            case 2: return obj2;
++            case 3: return obj3;
++            default: return rest[index - 4];
++        }
++    }
++
++    /**
++     * Returns the number of objects in this instance.
++     *
++     * @return the number of objects in this instance
++     */
++    public int size() {
++        return size;
++    }
++
++    /** {@inheritDoc} */
++    public final int hashCode() {
++        Enumeration e = this.getObjects();
++        int hashCode = 0;
++
++        while (e.hasMoreElements()) {
++            Object o = e.nextElement();
++            
++            if (o != null) {
++                hashCode ^= o.hashCode();
++            }
++        }
++
++        return hashCode;
++    }
++
++    /**
++     * Adds a new element to this instance.
++     * 
++     * @param obj non-null; the instance to add
++     */
++    protected void addObject(DEREncodable obj) {
++        if (obj == null) {
++            throw new NullPointerException("obj == null");
++        }
++
++        int sz = size;
++        
++        switch (sz) {
++            case 0: obj0 = obj; break;
++            case 1: obj1 = obj; break;
++            case 2: obj2 = obj; break;
++            case 3: obj3 = obj; break;
++            case 4: {
++                // Initial allocation of rest.
++                rest = new DEREncodable[5];
++                rest[0] = obj;
++                break;
++            }
++            default: {
++                int index = sz - 4;
++                if (index >= rest.length) {
++                    // Grow rest.
++                    DEREncodable[] newRest = new DEREncodable[index * 2 + 10];
++                    System.arraycopy(rest, 0, newRest, 0, rest.length);
++                    rest = newRest;
++                }
++                rest[index] = obj;
++                break;
++            }
++        }
++
++        size++;
++    }
++
++    /**
++     * Sets the element at a given index (used by {@link #sort}).
++     * 
++     * @param obj non-null; the object to set
++     * @param index &gt;= 0; the index
++     */
++    private void setObjectAt(DEREncodable obj, int index) {
++        switch (index) {
++            case 0: obj0 = obj; break;
++            case 1: obj1 = obj; break;
++            case 2: obj2 = obj; break;
++            case 3: obj3 = obj; break;
++            default: {
++                rest[index - 4] = obj;
++                break;
++            }
++        }
++    }
++
++    /**
++     * Encodes this instance to the given stream.
++     * 
++     * @param out non-null; stream to encode to
++     */
++    /*package*/ abstract void encode(DEROutputStream out) throws IOException;
++
++    /**
++     * Gets an enumeration of all the objects in this collection.
++     * 
++     * @return non-null; the enumeration
++     */
++    public Enumeration getObjects() {
++        return new ASN1CollectionEnumeration();
++    }
++
++    /**
++     * Associated enumeration class.
++     */
++    private class ASN1CollectionEnumeration implements Enumeration {
++        /** original size; used for modification detection */
++        private final int origSize = size;
++
++        /** &gt;= 0; current cursor */
++        private int at = 0;
++
++        /** {@inheritDoc} */
++        public boolean hasMoreElements() {
++            if (size != origSize) {
++                throw new ConcurrentModificationException();
++            }
++
++            return at < origSize;
++        }
++
++        /** {@inheritDoc} */
++        public Object nextElement() {
++            if (size != origSize) {
++                throw new ConcurrentModificationException();
++            }
++
++            switch (at++) {
++                case 0: return obj0;
++                case 1: return obj1;
++                case 2: return obj2;
++                case 3: return obj3;
++                default: return rest[at - 5];
++            }
++        }
++    }
++
++    /**
++     * Sorts the elements in this instance.
++     */
++    protected void sort() {
++        if (size <= 1) {
++            return;
++        }
++
++        boolean swapped = true;
++
++        // TODO: This is bubble sort. Probably not the best choice.
++        while (swapped) {
++            int index = 0;
++            byte[] a = getEncoded(getObjectAt(0));
++                
++            swapped = false;
++                
++            while (index != size - 1) {
++                int nextIndex = index + 1;
++                byte[] b = getEncoded(getObjectAt(nextIndex));
++
++                if (lessThanOrEqual(a, b)) {
++                    a = b;
++                } else {
++                    DEREncodable o = getObjectAt(index);
++                    
++                    setObjectAt(getObjectAt(nextIndex), index);
++                    setObjectAt(o, nextIndex);
++
++                    swapped = true;
++                }
++
++                index++;
++            }
++        }
++    }
++    
++    /**
++     * Returns true if a <= b (arrays are assumed padded with zeros).
++     */
++    private static boolean lessThanOrEqual(byte[] a, byte[] b) {
++        if (a.length <= b.length) {
++            for (int i = 0; i != a.length; i++) {
++                int l = a[i] & 0xff;
++                int r = b[i] & 0xff;
++                 
++                if (r > l) {
++                    return true;
++                } else if (l > r) {
++                    return false;
++                }
++            }
++
++            return true;
++        } else {
++            for (int i = 0; i != b.length; i++) {
++                 int l = a[i] & 0xff;
++                 int r = b[i] & 0xff;
++                 
++                 if (r > l) {
++                     return true;
++                 } else if (l > r) {
++                     return false;
++                 }
++             }
++
++             return false;
++         }
++    }
++
++    /**
++     * Gets the encoded form of an object.
++     * 
++     * @param obj non-null; object to encode
++     * @return non-null; the encoded form
++     */
++    private static byte[] getEncoded(DEREncodable obj) {
++        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
++        ASN1OutputStream aOut = new ASN1OutputStream(bOut);
++
++        try {
++            aOut.writeObject(obj);
++        } catch (IOException e) {
++            throw new IllegalArgumentException(
++                    "cannot encode object added to collection");
++        }
++
++        return bOut.toByteArray();
++    }
++
++    /** {@inheritDoc} */
++    public final String toString() {
++        StringBuilder sb = new StringBuilder();
++        sb.append('[');
++        for (int i = 0; i < size; i++) {
++            if (i != 0) sb.append(", ");
++            sb.append(getObjectAt(i));
++        }
++        sb.append(']');
++        return sb.toString();
++    }
++}
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1InputStream.java bcprov-jdk16-145/org/bouncycastle/asn1/ASN1InputStream.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1InputStream.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/ASN1InputStream.java	2010-06-25 23:17:26.000000000 +0000
+@@ -348,7 +348,9 @@
+             case BMP_STRING:
+                 return new DERBMPString(bytes);
+             case BOOLEAN:
+-                return new DERBoolean(bytes);
++                // BEGIN android-changed
++                return DERBoolean.getInstance(bytes);
++                // END android-changed
+             case ENUMERATED:
+                 return new DEREnumerated(bytes);
+             case GENERALIZED_TIME:
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Null.java bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Null.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Null.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Null.java	2010-06-25 23:17:26.000000000 +0000
+@@ -8,9 +8,11 @@
+ public abstract class ASN1Null
+     extends ASN1Object
+ {
+-    public ASN1Null()
++    // BEGIN android-changed
++    /*package*/ ASN1Null()
+     {
+     }
++    // END android-changed
+ 
+     public int hashCode()
+     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Sequence.java bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Sequence.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Sequence.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Sequence.java	2010-06-25 23:17:26.000000000 +0000
+@@ -2,12 +2,20 @@
+ 
+ import java.io.IOException;
+ import java.util.Enumeration;
+-import java.util.Vector;
++// BEGIN android-removed
++// import java.util.Vector;
++// END android-removed
++
++// BEGIN android-note
++// Changed inheritence of class.
++// END android-note
+ 
+ public abstract class ASN1Sequence
+-    extends ASN1Object
++    extends ASN1Collection
+ {
+-    private Vector seq = new Vector();
++    // BEGIN android-removed
++    // private Vector seq = new Vector();
++    // END android-removed
+ 
+     /**
+      * return an ASN1Sequence from the given object.
+@@ -85,10 +93,12 @@
+         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+     }
+ 
+-    public Enumeration getObjects()
+-    {
+-        return seq.elements();
+-    }
++    // BEGIN android-removed
++    // public Enumeration getObjects()
++    // {
++    //     return seq.elements();
++    // }
++    // END android-removed
+ 
+     public ASN1SequenceParser parser()
+     {
+@@ -127,45 +137,47 @@
+         };
+     }
+ 
+-    /**
+-     * return the object at the sequence position indicated by index.
+-     *
+-     * @param index the sequence number (starting at zero) of the object
+-     * @return the object at the sequence position indicated by index.
+-     */
+-    public DEREncodable getObjectAt(
+-        int index)
+-    {
+-        return (DEREncodable)seq.elementAt(index);
+-    }
+-
+-    /**
+-     * return the number of objects in this sequence.
+-     *
+-     * @return the number of objects in this sequence.
+-     */
+-    public int size()
+-    {
+-        return seq.size();
+-    }
+-
+-    public int hashCode()
+-    {
+-        Enumeration             e = this.getObjects();
+-        int                     hashCode = size();
+-
+-        while (e.hasMoreElements())
+-        {
+-            Object o = e.nextElement();
+-            hashCode *= 17;
+-            if (o != null)
+-            {
+-                hashCode ^= o.hashCode();
+-            }
+-        }
+-
+-        return hashCode;
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * return the object at the sequence position indicated by index.
++    //  *
++    //  * @param index the sequence number (starting at zero) of the object
++    //  * @return the object at the sequence position indicated by index.
++    //  */
++    // public DEREncodable getObjectAt(
++    //     int index)
++    // {
++    //     return (DEREncodable)seq.elementAt(index);
++    // }
++    //
++    // /**
++    //  * return the number of objects in this sequence.
++    //  *
++    //  * @return the number of objects in this sequence.
++    //  */
++    // public int size()
++    // {
++    //     return seq.size();
++    // }
++    //
++    // public int hashCode()
++    // {
++    //     Enumeration             e = this.getObjects();
++    //     int                     hashCode = size();
++    //
++    //     while (e.hasMoreElements())
++    //     {
++    //         Object o = e.nextElement();
++    //         hashCode *= 17;
++    //         if (o != null)
++    //         {
++    //             hashCode ^= o.hashCode();
++    //         }
++    //     }
++    //
++    //     return hashCode;
++    // }
++    // END android-removed
+ 
+     boolean asn1Equals(
+         DERObject  o)
+@@ -201,17 +213,19 @@
+         return true;
+     }
+ 
+-    protected void addObject(
+-        DEREncodable obj)
+-    {
+-        seq.addElement(obj);
+-    }
+-
+-    abstract void encode(DEROutputStream out)
+-        throws IOException;
+-
+-    public String toString() 
+-    {
+-      return seq.toString();
+-    }
++    // BEGIN android-removed
++    //protected void addObject(
++    //    DEREncodable obj)
++    //{
++    //    seq.addElement(obj);
++    //}
++
++    //abstract void encode(DEROutputStream out)
++    //    throws IOException;
++
++    //public String toString() 
++    //{
++    //  return seq.toString();
++    //}
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Set.java bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Set.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/ASN1Set.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/ASN1Set.java	2010-06-25 23:17:26.000000000 +0000
+@@ -3,12 +3,20 @@
+ import java.io.ByteArrayOutputStream;
+ import java.io.IOException;
+ import java.util.Enumeration;
+-import java.util.Vector;
++// BEGIN android-removed
++// import java.util.Vector;
++// END android-removed
++
++// BEGIN android-note
++// Changed inheritence of class.
++// END android-note
+ 
+ abstract public class ASN1Set
+-    extends ASN1Object
++    extends ASN1Collection
+ {
+-    protected Vector set = new Vector();
++    // BEGIN android-removed
++    // protected Vector set = new Vector();
++    // END android-removed
+ 
+     /**
+      * return an ASN1Set from the given object.
+@@ -104,32 +112,34 @@
+     {
+     }
+ 
+-    public Enumeration getObjects()
+-    {
+-        return set.elements();
+-    }
+-
+-    /**
+-     * return the object at the set position indicated by index.
+-     *
+-     * @param index the set number (starting at zero) of the object
+-     * @return the object at the set position indicated by index.
+-     */
+-    public DEREncodable getObjectAt(
+-        int index)
+-    {
+-        return (DEREncodable)set.elementAt(index);
+-    }
+-
+-    /**
+-     * return the number of objects in this set.
+-     *
+-     * @return the number of objects in this set.
+-     */
+-    public int size()
+-    {
+-        return set.size();
+-    }
++    // BEGIN android-removed
++    // public Enumeration getObjects()
++    // {
++    //     return set.elements();
++    // }
++    //
++    // /**
++    //  * return the object at the set position indicated by index.
++    //  *
++    //  * @param index the set number (starting at zero) of the object
++    //  * @return the object at the set position indicated by index.
++    //  */
++    // public DEREncodable getObjectAt(
++    //     int index)
++    // {
++    //     return (DEREncodable)set.elementAt(index);
++    // }
++    //
++    // /**
++    //  * return the number of objects in this set.
++    //  *
++    //  * @return the number of objects in this set.
++    //  */
++    // public int size()
++    // {
++    //     return set.size();
++    // }
++    // END android-removed
+ 
+     public ASN1SetParser parser()
+     {
+@@ -168,23 +178,25 @@
+         };
+     }
+ 
+-    public int hashCode()
+-    {
+-        Enumeration             e = this.getObjects();
+-        int                     hashCode = size();
+-
+-        while (e.hasMoreElements())
+-        {
+-            Object o = e.nextElement();
+-            hashCode *= 17;
+-            if (o != null)
+-            {
+-                hashCode ^= o.hashCode();
+-            }
+-        }
+-
+-        return hashCode;
+-    }
++    // BEGIN android-removed
++    // public int hashCode()
++    // {
++    //     Enumeration             e = this.getObjects();
++    //     int                     hashCode = size();
++    //
++    //     while (e.hasMoreElements())
++    //     {
++    //         Object o = e.nextElement();
++    //         hashCode *= 17;
++    //         if (o != null)
++    //         {
++    //             hashCode ^= o.hashCode();
++    //         }
++    //     }
++    //
++    //     return hashCode;
++    // }
++    // END android-removed
+ 
+     boolean asn1Equals(
+         DERObject  o)
+@@ -220,52 +232,54 @@
+         return true;
+     }
+ 
+-    /**
+-     * return true if a <= b (arrays are assumed padded with zeros).
+-     */
+-    private boolean lessThanOrEqual(
+-         byte[] a,
+-         byte[] b)
+-    {
+-         if (a.length <= b.length)
+-         {
+-             for (int i = 0; i != a.length; i++)
+-             {
+-                 int    l = a[i] & 0xff;
+-                 int    r = b[i] & 0xff;
+-                 
+-                 if (r > l)
+-                 {
+-                     return true;
+-                 }
+-                 else if (l > r)
+-                 {
+-                     return false;
+-                 }
+-             }
+-
+-             return true;
+-         }
+-         else
+-         {
+-             for (int i = 0; i != b.length; i++)
+-             {
+-                 int    l = a[i] & 0xff;
+-                 int    r = b[i] & 0xff;
+-                 
+-                 if (r > l)
+-                 {
+-                     return true;
+-                 }
+-                 else if (l > r)
+-                 {
+-                     return false;
+-                 }
+-             }
+-
+-             return false;
+-         }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * return true if a <= b (arrays are assumed padded with zeros).
++    //  */
++    // private boolean lessThanOrEqual(
++    //      byte[] a,
++    //      byte[] b)
++    // {
++    //      if (a.length <= b.length)
++    //      {
++    //          for (int i = 0; i != a.length; i++)
++    //          {
++    //              int    l = a[i] & 0xff;
++    //              int    r = b[i] & 0xff;
++    //
++    //              if (r > l)
++    //              {
++    //                  return true;
++    //              }
++    //              else if (l > r)
++    //              {
++    //                  return false;
++    //              }
++    //          }
++    //
++    //          return true;
++    //      }
++    //      else
++    //      {
++    //          for (int i = 0; i != b.length; i++)
++    //          {
++    //              int    l = a[i] & 0xff;
++    //              int    r = b[i] & 0xff;
++    //
++    //              if (r > l)
++    //              {
++    //                  return true;
++    //              }
++    //              else if (l > r)
++    //              {
++    //                  return false;
++    //              }
++    //          }
++    //
++    //          return false;
++    //      }
++    // }
++    // END android-removed
+ 
+     private byte[] getEncoded(
+         DEREncodable obj)
+@@ -285,59 +299,61 @@
+         return bOut.toByteArray();
+     }
+ 
+-    protected void sort()
+-    {
+-        if (set.size() > 1)
+-        {
+-            boolean    swapped = true;
+-            int        lastSwap = set.size() - 1;
+-
+-            while (swapped)
+-            {
+-                int    index = 0;
+-                int    swapIndex = 0;
+-                byte[] a = getEncoded((DEREncodable)set.elementAt(0));
+-                
+-                swapped = false;
+-
+-                while (index != lastSwap)
+-                {
+-                    byte[] b = getEncoded((DEREncodable)set.elementAt(index + 1));
+-
+-                    if (lessThanOrEqual(a, b))
+-                    {
+-                        a = b;
+-                    }
+-                    else
+-                    {
+-                        Object  o = set.elementAt(index);
+-
+-                        set.setElementAt(set.elementAt(index + 1), index);
+-                        set.setElementAt(o, index + 1);
+-
+-                        swapped = true;
+-                        swapIndex = index;
+-                    }
+-
+-                    index++;
+-                }
+-
+-                lastSwap = swapIndex;
+-            }
+-        }
+-    }
+-
+-    protected void addObject(
+-        DEREncodable obj)
+-    {
+-        set.addElement(obj);
+-    }
+-
+-    abstract void encode(DEROutputStream out)
+-            throws IOException;
+-
+-    public String toString() 
+-    {
+-      return set.toString();
+-    }
++    // BEGIN android-removed
++    // protected void sort()
++    // {
++    //     if (set.size() > 1)
++    //     {
++    //         boolean    swapped = true;
++    //         int        lastSwap = set.size() - 1;
++    //
++    //         while (swapped)
++    //         {
++    //             int    index = 0;
++    //             int    swapIndex = 0;
++    //             byte[] a = getEncoded((DEREncodable)set.elementAt(0));
++    //
++    //             swapped = false;
++    //
++    //             while (index != lastSwap)
++    //             {
++    //                 byte[] b = getEncoded((DEREncodable)set.elementAt(index + 1));
++    //
++    //                 if (lessThanOrEqual(a, b))
++    //                 {
++    //                     a = b;
++    //                 }
++    //                 else
++    //                 {
++    //                     Object  o = set.elementAt(index);
++    //
++    //                     set.setElementAt(set.elementAt(index + 1), index);
++    //                     set.setElementAt(o, index + 1);
++    //
++    //                     swapped = true;
++    //                     swapIndex = index;
++    //                 }
++    //
++    //                 index++;
++    //             }
++    //
++    //             lastSwap = swapIndex;
++    //         }
++    //     }
++    // }
++    //
++    // protected void addObject(
++    //     DEREncodable obj)
++    // {
++    //     set.addElement(obj);
++    // }
++    //
++    // abstract void encode(DEROutputStream out)
++    //         throws IOException;
++    //
++    // public String toString() 
++    // {
++    //   return set.toString();
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERBoolean.java bcprov-jdk16-145/org/bouncycastle/asn1/DERBoolean.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERBoolean.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/DERBoolean.java	2010-06-25 23:17:26.000000000 +0000
+@@ -5,7 +5,9 @@
+ public class DERBoolean
+     extends ASN1Object
+ {
+-    byte         value;
++    // BEGIN android-changed
++    private final byte  value;
++    // END android-changed
+ 
+     public static final DERBoolean FALSE = new DERBoolean(false);
+     public static final DERBoolean TRUE  = new DERBoolean(true);
+@@ -25,7 +27,9 @@
+ 
+         if (obj instanceof ASN1OctetString)
+         {
+-            return new DERBoolean(((ASN1OctetString)obj).getOctets());
++            // BEGIN android-changed
++            return getInstance(((ASN1OctetString)obj).getOctets());
++            // END android-changed
+         }
+ 
+         if (obj instanceof ASN1TaggedObject)
+@@ -45,6 +49,17 @@
+         return (value ? TRUE : FALSE);
+     }
+ 
++    // BEGIN android-added
++    /**
++     * return a DERBoolean from the passed in array.
++     */
++    public static DERBoolean getInstance(
++        byte[] octets)
++    {
++        return (octets[0] != 0) ? TRUE : FALSE;
++    }
++    // END android-added
++
+     /**
+      * return a Boolean from a tagged object.
+      *
+@@ -60,18 +75,22 @@
+     {
+         return getInstance(obj.getObject());
+     }
+-    
+-    public DERBoolean(
+-        byte[]       value)
+-    {
+-        this.value = value[0];
+-    }
+ 
+-    public DERBoolean(
++    // BEGIN android-removed
++    //private DERBoolean(
++    //    byte[]       value)
++    //{
++    //    this.value = value[0];
++    //}
++    // END android-removed
++
++    // BEGIN android-changed
++    private DERBoolean(
+         boolean     value)
+     {
+         this.value = (value) ? (byte)0xff : (byte)0;
+     }
++    // END android-changed
+ 
+     public boolean isTrue()
+     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERInputStream.java bcprov-jdk16-145/org/bouncycastle/asn1/DERInputStream.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERInputStream.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/DERInputStream.java	2010-06-25 23:17:26.000000000 +0000
+@@ -144,7 +144,9 @@
+                 return new DERConstructedSet(v);
+             }
+         case BOOLEAN:
+-            return new DERBoolean(bytes);
++            // BEGIN android-changed
++            return DERBoolean.getInstance(bytes);
++            // BEGIN android-changed
+         case INTEGER:
+             return new DERInteger(bytes);
+         case ENUMERATED:
+@@ -195,7 +197,9 @@
+                 {
+                     if ((tag & CONSTRUCTED) == 0)
+                     {
+-                        return new DERTaggedObject(false, tag & 0x1f, new DERNull());
++                        // BEGIN android-changed
++                        return new DERTaggedObject(false, tag & 0x1f, DERNull.INSTANCE);
++                        // END android-changed
+                     }
+                     else
+                     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERNull.java bcprov-jdk16-145/org/bouncycastle/asn1/DERNull.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERNull.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/DERNull.java	2010-06-25 23:17:26.000000000 +0000
+@@ -10,9 +10,13 @@
+ {
+     public static final DERNull INSTANCE = new DERNull();
+ 
+-    byte[]  zeroBytes = new byte[0];
++    // BEGIN android-changed
++    private static final byte[]  zeroBytes = new byte[0];
++    // END android-changed
+ 
+-    public DERNull()
++    // BEGIN android-changed
++    protected DERNull()
++    // END android-changed
+     {
+     }
+ 
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERObjectIdentifier.java bcprov-jdk16-145/org/bouncycastle/asn1/DERObjectIdentifier.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERObjectIdentifier.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/DERObjectIdentifier.java	2010-06-25 23:17:26.000000000 +0000
+@@ -111,7 +111,13 @@
+             }
+         }
+ 
+-        this.identifier = objId.toString();
++        // BEGIN android-changed
++        /*
++         * Intern the identifier so there aren't hundreds of duplicates
++         * (in practice).
++         */
++        this.identifier = objId.toString().intern();
++        // END android-changed
+     }
+ 
+     public DERObjectIdentifier(
+@@ -122,7 +128,13 @@
+             throw new IllegalArgumentException("string " + identifier + " not an OID");
+         }
+ 
+-        this.identifier = identifier;
++        // BEGIN android-changed
++        /*
++         * Intern the identifier so there aren't hundreds of duplicates
++         * (in practice).
++         */
++        this.identifier = identifier.intern();
++        // END android-changed
+     }
+ 
+     public String getId()
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERPrintableString.java bcprov-jdk16-145/org/bouncycastle/asn1/DERPrintableString.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/DERPrintableString.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/DERPrintableString.java	2010-06-25 23:17:26.000000000 +0000
+@@ -9,7 +9,9 @@
+     extends ASN1Object
+     implements DERString
+ {
+-    String  string;
++    // BEGIN android-changed
++    private final String string;
++    // END android-changed
+ 
+     /**
+      * return a printable string from the passed in object.
+@@ -66,7 +68,9 @@
+             cs[i] = (char)(string[i] & 0xff);
+         }
+ 
+-        this.string = new String(cs);
++        // BEGIN android-changed
++        this.string = new String(cs).intern();
++        // END android-changed
+     }
+ 
+     /**
+@@ -95,7 +99,9 @@
+             throw new IllegalArgumentException("string contains illegal characters");
+         }
+ 
+-        this.string = string;
++        // BEGIN android-changed
++        this.string = string.intern();
++        // END android-changed
+     }
+ 
+     public String getString()
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/OrderedTable.java bcprov-jdk16-145/org/bouncycastle/asn1/OrderedTable.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/OrderedTable.java	1970-01-01 00:00:00.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/OrderedTable.java	2010-06-25 23:17:26.000000000 +0000
+@@ -0,0 +1,281 @@
++package org.bouncycastle.asn1;
++
++import java.util.Enumeration;
++import java.util.ConcurrentModificationException;
++
++// BEGIN android-note
++/*
++ * This is a new class that was synthesized from the observed
++ * requirement for a lookup table that preserves order. Since in
++ * practice the element count is typically very low, we just use a
++ * flat list rather than doing any hashing / bucketing.
++ */
++// END android-note
++
++/**
++ * Ordered lookup table. Instances of this class will keep up to four
++ * key-value pairs directly, resorting to an external collection only
++ * if more elements than that need to be stored.
++ */
++public final class OrderedTable {
++    /** null-ok; key #0 */
++    private DERObjectIdentifier key0;
++    
++    /** null-ok; key #1 */
++    private DERObjectIdentifier key1;
++
++    /** null-ok; key #2 */
++    private DERObjectIdentifier key2;
++
++    /** null-ok; key #3 */
++    private DERObjectIdentifier key3;
++    
++    /** null-ok; value #0 */
++    private Object value0;
++    
++    /** null-ok; value #1 */
++    private Object value1;
++
++    /** null-ok; value #2 */
++    private Object value2;
++
++    /** null-ok; value #3 */
++    private Object value3;
++    
++    /**
++     * null-ok; array of additional keys and values, alternating
++     * key then value, etc. 
++     */
++    private Object[] rest;
++
++    /** &gt;= 0; number of elements in the list */
++    private int size;
++
++    // Note: Default public constructor.
++
++    /**
++     * Adds an element assuming no duplicate key.
++     * 
++     * @see #put
++     * 
++     * @param key non-null; the key
++     * @param value non-null; the value
++     */
++    public void add(DERObjectIdentifier key, Object value) {
++        if (key == null) {
++            throw new NullPointerException("key == null");
++        }
++
++        if (value == null) {
++            throw new NullPointerException("value == null");
++        }
++
++        int sz = size;
++
++        switch (sz) {
++            case 0: {
++                key0 = key;
++                value0 = value;
++                break;
++            }
++            case 1: {
++                key1 = key;
++                value1 = value;
++                break;
++            }
++            case 2: {
++                key2 = key;
++                value2 = value;
++                break;
++            }
++            case 3: {
++                key3 = key;
++                value3 = value;
++                break;
++            }
++            case 4: {
++                // Do initial allocation of rest.
++                rest = new Object[10];
++                rest[0] = key;
++                rest[1] = value;
++                break;
++            }
++            default: {
++                int index = (sz - 4) * 2;
++                int index1 = index + 1;
++                if (index1 >= rest.length) {
++                    // Grow rest.
++                    Object[] newRest = new Object[index1 * 2 + 10];
++                    System.arraycopy(rest, 0, newRest, 0, rest.length);
++                    rest = newRest;
++                }
++                rest[index] = key;
++                rest[index1] = value;
++                break;
++            }
++        }
++        
++        size = sz + 1;
++    }
++
++    /**
++     * Gets the number of elements in this instance.
++     */
++    public int size() {
++        return size;
++    }
++
++    /**
++     * Look up the given key, returning the associated value if found.
++     * 
++     * @param key non-null; the key to look up
++     * @return null-ok; the associated value
++     */
++    public Object get(DERObjectIdentifier key) {
++        int keyHash = key.hashCode();
++        int sz = size;
++
++        for (int i = 0; i < size; i++) {
++            DERObjectIdentifier probe = getKey(i);
++            if ((probe.hashCode() == keyHash) &&
++                    probe.equals(key)) {
++                return getValue(i);
++            }
++        }
++
++        return null;
++    }
++    
++    /**
++     * Replace a key if present, otherwise add
++     * 
++     * @see #add
++     * 
++     * @param key non-null; the key
++     * @param value non-null; the value
++     */
++    public void put(DERObjectIdentifier key, Object value) {
++        if (key == null) {
++            throw new NullPointerException("key == null");
++        }
++
++        if (value == null) {
++            throw new NullPointerException("value == null");
++        }
++
++        int keyHash = key.hashCode();
++        int sz = size;
++
++        for (int i = 0; i < size; i++) {
++            DERObjectIdentifier probe = getKey(i);
++            if ((probe.hashCode() == keyHash) &&
++                    probe.equals(key)) {
++                setValue(i, value);
++                return;
++            }
++        }
++
++        add(key, value);
++    }
++    
++    /**
++     * Gets the nth key.
++     * 
++     * @param n index
++     * @return non-null; the nth key
++     */
++    public DERObjectIdentifier getKey(int n) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++
++        switch (n) {
++            case 0: return key0;
++            case 1: return key1;
++            case 2: return key2;
++            case 3: return key3;
++            default: return (DERObjectIdentifier) rest[(n - 4) * 2];
++        }
++    }
++
++    /**
++     * Gets the nth value.
++     * 
++     * @param n index
++     * @return non-null; the nth value
++     */
++    public Object getValue(int n) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++
++        switch (n) {
++            case 0: return value0;
++            case 1: return value1;
++            case 2: return value2;
++            case 3: return value3;
++            default: return rest[((n - 4) * 2) + 1];
++        }
++    }
++
++    /**
++     * Sets the nth value.
++     * 
++     * @param n index
++     * @param value non-null object
++     */
++    public void setValue(int n, Object value) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++        if (value == null) {
++            throw new NullPointerException("value == null");
++        }
++
++        switch (n) {
++            case 0: value0 = value; return;
++            case 1: value1 = value; return;
++            case 2: value2 = value; return;
++            case 3: value3 = value; return;
++            default: rest[((n - 4) * 2) + 1] = value; return;
++        }
++    }
++
++    /**
++     * Gets an enumeration of the keys, in order.
++     * 
++     * @return non-null; an enumeration of the keys
++     */
++    public Enumeration getKeys() {
++        return new KeyEnumeration();
++    }
++
++    /**
++     * Associated enumeration class.
++     */
++    private class KeyEnumeration implements Enumeration {
++        /** original size; used for modification detection */
++        private final int origSize = size;
++
++        /** &gt;= 0; current cursor */
++        private int at = 0;
++
++        /** {@inheritDoc} */
++        public boolean hasMoreElements() {
++            if (size != origSize) {
++                throw new ConcurrentModificationException();
++            }
++
++            return at < origSize;
++        }
++
++        /** {@inheritDoc} */
++        public Object nextElement() {
++            if (size != origSize) {
++                throw new ConcurrentModificationException();
++            }
++
++            return getKey(at++);
++        }
++    }
++}
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java	2010-06-25 23:17:26.000000000 +0000
+@@ -10,7 +10,10 @@
+     //
+     static final String                 pkcs_1                    = "1.2.840.113549.1.1";
+     static final DERObjectIdentifier    rsaEncryption             = new DERObjectIdentifier(pkcs_1 + ".1");
+-    static final DERObjectIdentifier    md2WithRSAEncryption      = new DERObjectIdentifier(pkcs_1 + ".2");
++    // BEGIN android-removed
++    // Dropping MD2
++    // static final DERObjectIdentifier    md2WithRSAEncryption      = new DERObjectIdentifier(pkcs_1 + ".2");
++    // END android-removed
+     static final DERObjectIdentifier    md4WithRSAEncryption      = new DERObjectIdentifier(pkcs_1 + ".3");
+     static final DERObjectIdentifier    md5WithRSAEncryption      = new DERObjectIdentifier(pkcs_1 + ".4");
+     static final DERObjectIdentifier    sha1WithRSAEncryption     = new DERObjectIdentifier(pkcs_1 + ".5");
+@@ -46,7 +49,9 @@
+ 
+     static final DERObjectIdentifier    id_PBES2                = new DERObjectIdentifier(pkcs_5 + ".13");
+ 
+-    static final DERObjectIdentifier    id_PBKDF2               = new DERObjectIdentifier(pkcs_5 + ".12");
++    // BEGIN android-removed
++    // static final DERObjectIdentifier    id_PBKDF2               = new DERObjectIdentifier(pkcs_5 + ".12");
++    // END android-removed
+ 
+     //
+     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
+@@ -65,7 +70,10 @@
+     // md2 OBJECT IDENTIFIER ::=
+     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
+     //
+-    static final DERObjectIdentifier    md2                     = new DERObjectIdentifier(digestAlgorithm + ".2");
++    // BEGIN android-removed
++    // Dropping MD2
++    // static final DERObjectIdentifier    md2                     = new DERObjectIdentifier(digestAlgorithm + ".2");
++    // END android-removed
+ 
+     //
+     // md4 OBJECT IDENTIFIER ::=
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java	2010-06-25 23:17:26.000000000 +0000
+@@ -19,7 +19,9 @@
+     private AlgorithmIdentifier maskGenAlgorithm;
+     private AlgorithmIdentifier pSourceAlgorithm;
+     
+-    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, new DERNull());
++    // BEGIN android-changed
++    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
++    // END android-changed
+     public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
+     public final static AlgorithmIdentifier DEFAULT_P_SOURCE_ALGORITHM = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0]));
+     
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java	2010-06-25 23:17:26.000000000 +0000
+@@ -20,7 +20,9 @@
+     private DERInteger          saltLength;
+     private DERInteger          trailerField;
+     
+-    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, new DERNull());
++    // BEGIN android-changed
++    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
++    // END android-changed
+     public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
+     public final static DERInteger          DEFAULT_SALT_LENGTH = new DERInteger(20);
+     public final static DERInteger          DEFAULT_TRAILER_FIELD = new DERInteger(1);
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/util/ASN1Dump.java bcprov-jdk16-145/org/bouncycastle/asn1/util/ASN1Dump.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/util/ASN1Dump.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/util/ASN1Dump.java	2010-06-25 23:17:26.000000000 +0000
+@@ -90,7 +90,9 @@
+             {
+                 Object  o = e.nextElement();
+ 
+-                if (o == null || o.equals(new DERNull()))
++                // BEGIN android-changed
++                if (o == null || o.equals(DERNull.INSTANCE))
++                // END android-changed
+                 {
+                     buf.append(tab);
+                     buf.append("NULL");
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/AttCertIssuer.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/AttCertIssuer.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/AttCertIssuer.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/AttCertIssuer.java	2010-06-25 23:17:26.000000000 +0000
+@@ -45,7 +45,7 @@
+         ASN1TaggedObject obj,
+         boolean          explicit)
+     {
+-        return getInstance(obj.getObject()); // must be explictly tagged
++        return getInstance(obj.getObject()); // must be explicitly tagged
+     }
+ 
+     /**
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/BasicConstraints.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/BasicConstraints.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/BasicConstraints.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/BasicConstraints.java	2010-06-25 23:17:26.000000000 +0000
+@@ -14,7 +14,9 @@
+ public class BasicConstraints
+     extends ASN1Encodable
+ {
+-    DERBoolean  cA = new DERBoolean(false);
++    // BEGIN android-changed
++    DERBoolean  cA = DERBoolean.FALSE;
++    // END android-changed
+     DERInteger  pathLenConstraint = null;
+ 
+     public static BasicConstraints getInstance(
+@@ -89,7 +91,9 @@
+     {
+         if (cA)
+         {
+-            this.cA = new DERBoolean(cA);
++            // BEGIN android-changed
++            this.cA = DERBoolean.getInstance(cA);
++            // END android-changed
+             this.pathLenConstraint = new DERInteger(pathLenConstraint);
+         }
+         else
+@@ -104,7 +108,9 @@
+     {
+         if (cA)
+         {
+-            this.cA = new DERBoolean(true);
++            // BEGIN android-changed
++            this.cA = DERBoolean.TRUE;
++            // END android-changed
+         }
+         else
+         {
+@@ -121,7 +127,9 @@
+     public BasicConstraints(
+         int     pathLenConstraint)
+     {
+-        this.cA = new DERBoolean(true);
++        // BEGIN android-changed
++        this.cA = DERBoolean.TRUE;
++        // END android-changed
+         this.pathLenConstraint = new DERInteger(pathLenConstraint);
+     }
+ 
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java	2010-06-25 23:17:26.000000000 +0000
+@@ -96,11 +96,15 @@
+         }
+         if (onlyContainsUserCerts)
+         {
+-            vec.add(new DERTaggedObject(false, 1, new DERBoolean(true)));
++            // BEGIN android-changed
++            vec.add(new DERTaggedObject(false, 1, DERBoolean.TRUE));
++            // END android-changed
+         }
+         if (onlyContainsCACerts)
+         {
+-            vec.add(new DERTaggedObject(false, 2, new DERBoolean(true)));
++            // BEGIN android-changed
++            vec.add(new DERTaggedObject(false, 2, DERBoolean.TRUE));
++            // END android-changed
+         }
+         if (onlySomeReasons != null)
+         {
+@@ -108,11 +112,15 @@
+         }
+         if (indirectCRL)
+         {
+-            vec.add(new DERTaggedObject(false, 4, new DERBoolean(true)));
++            // BEGIN android-changed
++            vec.add(new DERTaggedObject(false, 4, DERBoolean.TRUE));
++            // END android-changed
+         }
+         if (onlyContainsAttributeCerts)
+         {
+-            vec.add(new DERTaggedObject(false, 5, new DERBoolean(true)));
++            // BEGIN android-changed
++            vec.add(new DERTaggedObject(false, 5, DERBoolean.TRUE));
++            // END android-changed
+         }
+ 
+         seq = new DERSequence(vec);
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509Extensions.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509Extensions.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509Extensions.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509Extensions.java	2010-06-25 23:17:26.000000000 +0000
+@@ -9,6 +9,9 @@
+ import org.bouncycastle.asn1.DERObject;
+ import org.bouncycastle.asn1.DERObjectIdentifier;
+ import org.bouncycastle.asn1.DERSequence;
++// BEGIN android-added
++import org.bouncycastle.asn1.OrderedTable;
++// END android-added
+ 
+ import java.util.Enumeration;
+ import java.util.Hashtable;
+@@ -172,8 +175,9 @@
+      */
+     public static final DERObjectIdentifier TargetInformation = new DERObjectIdentifier("2.5.29.55");
+     
+-    private Hashtable               extensions = new Hashtable();
+-    private Vector                  ordering = new Vector();
++    // BEGIN android-changed
++    private OrderedTable table = new OrderedTable();
++    // END android-changed
+ 
+     public static X509Extensions getInstance(
+         ASN1TaggedObject obj,
+@@ -217,20 +221,26 @@
+         {
+             ASN1Sequence            s = ASN1Sequence.getInstance(e.nextElement());
+ 
+-            if (s.size() == 3)
++            // BEGIN android-changed
++            int sSize = s.size();
++            DERObjectIdentifier key = (DERObjectIdentifier) s.getObjectAt(0);
++            Object value;
++            
++            if (sSize == 3)
+             {
+-                extensions.put(s.getObjectAt(0), new X509Extension(DERBoolean.getInstance(s.getObjectAt(1)), ASN1OctetString.getInstance(s.getObjectAt(2))));
++                value = new X509Extension(DERBoolean.getInstance(s.getObjectAt(1)), ASN1OctetString.getInstance(s.getObjectAt(2)));
+             }
+-            else if (s.size() == 2)
++            else if (sSize == 2)
+             {
+-                extensions.put(s.getObjectAt(0), new X509Extension(false, ASN1OctetString.getInstance(s.getObjectAt(1))));
++                value = new X509Extension(false, ASN1OctetString.getInstance(s.getObjectAt(1)));
+             }
+             else
+             {
+-                throw new IllegalArgumentException("Bad sequence size: " + s.size());
++                throw new IllegalArgumentException("Bad sequence size: " + sSize);
+             }
+ 
+-            ordering.addElement(s.getObjectAt(0));
++            table.add(key, value);
++            // END android-changed
+         }
+     }
+ 
+@@ -265,20 +275,14 @@
+             e = ordering.elements();
+         }
+ 
+-        while (e.hasMoreElements())
+-        {
+-            this.ordering.addElement(e.nextElement()); 
+-        }
+-
+-        e = this.ordering.elements();
+-
++        // BEGIN android-changed
+         while (e.hasMoreElements())
+         {
+             DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
+             X509Extension           ext = (X509Extension)extensions.get(oid);
+-
+-            this.extensions.put(oid, ext);
++            table.add(oid, ext);
+         }
++        // END android-changed
+     }
+ 
+     /**
+@@ -293,23 +297,18 @@
+     {
+         Enumeration e = objectIDs.elements();
+ 
+-        while (e.hasMoreElements())
+-        {
+-            this.ordering.addElement(e.nextElement()); 
+-        }
+-
++        // BEGIN android-changed
+         int count = 0;
+         
+-        e = this.ordering.elements();
+-
+         while (e.hasMoreElements())
+         {
+             DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
+             X509Extension           ext = (X509Extension)values.elementAt(count);
+ 
+-            this.extensions.put(oid, ext);
++            table.add(oid, ext);
+             count++;
+         }
++        // END android-changed
+     }
+     
+     /**
+@@ -317,7 +316,9 @@
+      */
+     public Enumeration oids()
+     {
+-        return ordering.elements();
++        // BEGIN android-changed
++        return table.getKeys();
++        // END android-changed
+     }
+ 
+     /**
+@@ -329,7 +330,9 @@
+     public X509Extension getExtension(
+         DERObjectIdentifier oid)
+     {
+-        return (X509Extension)extensions.get(oid);
++        // BEGIN android-changed
++        return (X509Extension)table.get(oid);
++        // END android-changed
+     }
+ 
+     /**
+@@ -345,19 +348,23 @@
+     public DERObject toASN1Object()
+     {
+         ASN1EncodableVector     vec = new ASN1EncodableVector();
+-        Enumeration             e = ordering.elements();
++        // BEGIN android-changed
++        int                     size = table.size();
+ 
+-        while (e.hasMoreElements())
++        for (int i = 0; i < size; i++)
+         {
+-            DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
+-            X509Extension           ext = (X509Extension)extensions.get(oid);
++            DERObjectIdentifier     oid = table.getKey(i);
++            X509Extension           ext = (X509Extension)table.getValue(i);
++            // END android-changed
+             ASN1EncodableVector     v = new ASN1EncodableVector();
+ 
+             v.add(oid);
+ 
+             if (ext.isCritical())
+             {
+-                v.add(new DERBoolean(true));
++                // BEGIN android-changed
++                v.add(DERBoolean.TRUE);
++                // END android-changed
+             }
+ 
+             v.add(ext.getValue());
+@@ -371,18 +378,24 @@
+     public boolean equivalent(
+         X509Extensions other)
+     {
+-        if (extensions.size() != other.extensions.size())
++        // BEGIN android-changed
++        if (table.size() != other.table.size())
++        // END android-changed
+         {
+             return false;
+         }
+ 
+-        Enumeration     e1 = extensions.keys();
++        // BEGIN android-changed
++        Enumeration     e1 = table.getKeys();
++        // END android-changed
+ 
+         while (e1.hasMoreElements())
+         {
+-            Object  key = e1.nextElement();
++            // BEGIN android-changed
++            DERObjectIdentifier  key = (DERObjectIdentifier)e1.nextElement();
+ 
+-            if (!extensions.get(key).equals(other.extensions.get(key)))
++            if (!table.get(key).equals(other.table.get(key)))
++            // END android-changed
+             {
+                 return false;
+             }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509Name.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509Name.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509Name.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509Name.java	2010-06-25 23:17:26.000000000 +0000
+@@ -247,8 +247,10 @@
+      */
+     public static final Hashtable SymbolLookUp = DefaultLookUp;
+ 
+-    private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
+-    private static final Boolean FALSE = new Boolean(false);
++    // BEGIN android-removed
++    //private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
++    //private static final Boolean FALSE = new Boolean(false);
++    // END android-removed
+ 
+     static
+     {
+@@ -340,9 +342,9 @@
+     }
+ 
+     private X509NameEntryConverter  converter = null;
+-    private Vector                  ordering = new Vector();
+-    private Vector                  values = new Vector();
+-    private Vector                  added = new Vector();
++    // BEGIN android-changed
++    private X509NameElementList     elems = new X509NameElementList();
++    // END android-changed
+ 
+     private ASN1Sequence            seq;
+ 
+@@ -403,26 +405,30 @@
+                        throw new IllegalArgumentException("badly sized pair");
+                    }
+ 
+-                   ordering.addElement(DERObjectIdentifier.getInstance(s.getObjectAt(0)));
++                   // BEGIN android-changed
++                   DERObjectIdentifier key = DERObjectIdentifier.getInstance(s.getObjectAt(0));
+                    
+                    DEREncodable value = s.getObjectAt(1);
++                   String valueStr;
+                    if (value instanceof DERString && !(value instanceof DERUniversalString))
+                    {
+                        String v = ((DERString)value).getString();
+                        if (v.length() > 0 && v.charAt(0) == '#')
+                        {
+-                           values.addElement("\\" + v);
++                           valueStr = "\\" + v;
+                        }
+                        else
+                        {
+-                           values.addElement(v);
++                           valueStr = v;
+                        }
+                    }
+                    else
+                    {
+-                       values.addElement("#" + bytesToString(Hex.encode(value.getDERObject().getDEREncoded())));
++                       valueStr = "#" + bytesToString(Hex.encode(value.getDERObject().getDEREncoded()));
+                    }
+-                   added.addElement((i != 0) ? TRUE : FALSE);  // to allow earlier JDK compatibility
++                   boolean added = (i != 0);  // to allow earlier JDK compatibility
++                   elems.add(key, valueStr, added);
++                   // END android-changed
+             }
+         }
+     }
+@@ -476,14 +482,23 @@
+         Hashtable                attributes,
+         X509NameEntryConverter   converter)
+     {
++        // BEGIN android-changed
++        DERObjectIdentifier problem = null;
+         this.converter = converter;
+ 
+         if (ordering != null)
+         {
+             for (int i = 0; i != ordering.size(); i++)
+             {
+-                this.ordering.addElement(ordering.elementAt(i));
+-                this.added.addElement(FALSE);
++                DERObjectIdentifier key =
++                    (DERObjectIdentifier) ordering.elementAt(i);
++                String value = (String) attributes.get(key);
++                if (value == null)
++                {
++                    problem = key;
++                    break;
++                }
++                elems.add(key, value);
+             }
+         }
+         else
+@@ -492,22 +507,23 @@
+ 
+             while (e.hasMoreElements())
+             {
+-                this.ordering.addElement(e.nextElement());
+-                this.added.addElement(FALSE);
++                DERObjectIdentifier key =
++                    (DERObjectIdentifier) e.nextElement();
++                String value = (String) attributes.get(key);
++                if (value == null)
++                {
++                    problem = key;
++                    break;
++                }
++                elems.add(key, value);
+             }
+         }
+ 
+-        for (int i = 0; i != this.ordering.size(); i++)
++        if (problem != null)
+         {
+-            DERObjectIdentifier     oid = (DERObjectIdentifier)this.ordering.elementAt(i);
+-
+-            if (attributes.get(oid) == null)
+-            {
+-                throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
+-            }
+-
+-            this.values.addElement(attributes.get(oid)); // copy the hash table
++            throw new IllegalArgumentException("No attribute for object id - " + problem.getId() + " - passed to distinguished name");
+         }
++        // END android-changed
+     }
+ 
+     /**
+@@ -540,9 +556,10 @@
+ 
+         for (int i = 0; i < oids.size(); i++)
+         {
+-            this.ordering.addElement(oids.elementAt(i));
+-            this.values.addElement(values.elementAt(i));
+-            this.added.addElement(FALSE);
++            // BEGIN android-changed
++            elems.add((DERObjectIdentifier) oids.elementAt(i),
++                    (String) values.elementAt(i));
++            // END android-changed
+         }
+     }
+ 
+@@ -679,7 +696,7 @@
+ 
+             if (index == -1)
+             {
+-                throw new IllegalArgumentException("badly formated directory string");
++                throw new IllegalArgumentException("badly formatted directory string");
+             }
+ 
+             String              name = token.substring(0, index);
+@@ -691,9 +708,9 @@
+                 X509NameTokenizer   vTok = new X509NameTokenizer(value, '+');
+                 String  v = vTok.nextToken();
+ 
+-                this.ordering.addElement(oid);
+-                this.values.addElement(v);
+-                this.added.addElement(FALSE);
++                // BEGIN android-changed
++                this.elems.add(oid, v);
++                // END android-changed
+ 
+                 while (vTok.hasMoreTokens())
+                 {
+@@ -702,48 +719,24 @@
+ 
+                     String  nm = sv.substring(0, ndx);
+                     String  vl = sv.substring(ndx + 1);
+-                    this.ordering.addElement(decodeOID(nm, lookUp));
+-                    this.values.addElement(vl);
+-                    this.added.addElement(TRUE);
++                    // BEGIN android-changed
++                    this.elems.add(decodeOID(nm, lookUp), vl, true);
++                    // END android-changed
+                 }
+             }
+             else
+             {
+-                this.ordering.addElement(oid);
+-                this.values.addElement(value);
+-                this.added.addElement(FALSE);
++                // BEGIN android-changed
++                this.elems.add(oid, value);
++                // END android-changed
+             }
+         }
+ 
+         if (reverse)
+         {
+-            Vector  o = new Vector();
+-            Vector  v = new Vector();
+-            Vector  a = new Vector();
+-
+-            int count = 1;
+-
+-            for (int i = 0; i < this.ordering.size(); i++)
+-            {
+-                if (((Boolean)this.added.elementAt(i)).booleanValue())
+-                {
+-                    o.insertElementAt(this.ordering.elementAt(i), count);
+-                    v.insertElementAt(this.values.elementAt(i), count);
+-                    a.insertElementAt(this.added.elementAt(i), count);
+-                    count++;
+-                }
+-                else
+-                {
+-                    o.insertElementAt(this.ordering.elementAt(i), 0);
+-                    v.insertElementAt(this.values.elementAt(i), 0);
+-                    a.insertElementAt(this.added.elementAt(i), 0);
+-                    count = 1;
+-                }
+-            }
+-
+-            this.ordering = o;
+-            this.values = v;
+-            this.added = a;
++            // BEGIN android-changed
++            this.elems = this.elems.reverse();
++            // END android-changed
+         }
+     }
+ 
+@@ -752,14 +745,17 @@
+      */
+     public Vector getOIDs()
+     {
++        // BEGIN android-changed
+         Vector  v = new Vector();
++        int     size = elems.size();
+ 
+-        for (int i = 0; i != ordering.size(); i++)
++        for (int i = 0; i < size; i++)
+         {
+-            v.addElement(ordering.elementAt(i));
++            v.addElement(elems.getKey(i));
+         }
+ 
+         return v;
++        // END android-changed
+     }
+ 
+     /**
+@@ -769,11 +765,14 @@
+     public Vector getValues()
+     {
+         Vector  v = new Vector();
++        // BEGIN android-changed
++        int     size = elems.size();
+ 
+-        for (int i = 0; i != values.size(); i++)
++        for (int i = 0; i != size; i++)
+         {
+-            v.addElement(values.elementAt(i));
++            v.addElement(elems.getValue(i));
+         }
++        // END android-changed
+ 
+         return v;
+     }
+@@ -786,12 +785,14 @@
+         DERObjectIdentifier oid)
+     {
+         Vector  v = new Vector();
++        int     size = elems.size();
++        // BEGIN android-changed
+ 
+-        for (int i = 0; i != values.size(); i++)
++        for (int i = 0; i != size; i++)
+         {
+-            if (ordering.elementAt(i).equals(oid))
++            if (elems.getKey(i).equals(oid))
+             {
+-                String val = (String)values.elementAt(i);
++                String val = elems.getValue(i);
+ 
+                 if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
+                 {
+@@ -803,6 +804,7 @@
+                 }
+             }
+         }
++        // END android-changed
+ 
+         return v;
+     }
+@@ -814,20 +816,23 @@
+             ASN1EncodableVector  vec = new ASN1EncodableVector();
+             ASN1EncodableVector  sVec = new ASN1EncodableVector();
+             DERObjectIdentifier  lstOid = null;
++            // BEGIN android-changed
++            int                  size = elems.size();
+             
+-            for (int i = 0; i != ordering.size(); i++)
++            for (int i = 0; i != size; i++)
+             {
+                 ASN1EncodableVector     v = new ASN1EncodableVector();
+-                DERObjectIdentifier     oid = (DERObjectIdentifier)ordering.elementAt(i);
++                DERObjectIdentifier     oid = elems.getKey(i);
+ 
+                 v.add(oid);
+ 
+-                String  str = (String)values.elementAt(i);
++                String  str = elems.getValue(i);
+ 
+                 v.add(converter.getConvertedValue(oid, str));
+  
+                 if (lstOid == null 
+-                    || ((Boolean)this.added.elementAt(i)).booleanValue())
++                    || this.elems.getAdded(i))
++                // END android-changed
+                 {
+                     sVec.add(new DERSequence(v));
+                 }
+@@ -845,6 +850,7 @@
+             vec.add(new DERSet(sVec));
+             
+             seq = new DERSequence(vec);
++            // END android-changed
+         }
+ 
+         return seq;
+@@ -889,22 +895,28 @@
+             return false;
+         }
+ 
+-        int      orderingSize = ordering.size();
++        // BEGIN android-changed
++        int      orderingSize = elems.size();
+ 
+-        if (orderingSize != other.ordering.size())
++        if (orderingSize != other.elems.size())
++        // END android-changed
+         {
+             return false;
+         }
+ 
+         for (int i = 0; i < orderingSize; i++)
+         {
+-            DERObjectIdentifier  oid = (DERObjectIdentifier)ordering.elementAt(i);
+-            DERObjectIdentifier  oOid = (DERObjectIdentifier)other.ordering.elementAt(i);
++            // BEGIN android-changed
++            DERObjectIdentifier  oid = elems.getKey(i);
++            DERObjectIdentifier  oOid = other.elems.getKey(i);
++            // END android-changed
+ 
+             if (oid.equals(oOid))
+             {
+-                String value = (String)values.elementAt(i);
+-                String oValue = (String)other.values.elementAt(i);
++                // BEGIN android-changed
++                String value = elems.getValue(i);
++                String oValue = other.elems.getValue(i);
++                // END android-changed
+ 
+                 if (!equivalentStrings(value, oValue))
+                 {
+@@ -930,9 +942,9 @@
+         isHashCodeCalculated = true;
+ 
+         // this needs to be order independent, like equals
+-        for (int i = 0; i != ordering.size(); i += 1)
++        for (int i = 0; i != elems.size(); i += 1)
+         {
+-            String value = (String)values.elementAt(i);
++            String value = (String)elems.getValue(i);
+ 
+             value = canonicalize(value);
+             value = stripInternalSpaces(value);
+@@ -976,9 +988,11 @@
+             return false;
+         }
+ 
+-        int      orderingSize = ordering.size();
++        // BEGIN android-changed
++        int      orderingSize = elems.size();
+ 
+-        if (orderingSize != other.ordering.size())
++        if (orderingSize != other.elems.size())
++            // END android-changed
+         {
+             return false;
+         }
+@@ -986,7 +1000,9 @@
+         boolean[] indexes = new boolean[orderingSize];
+         int       start, end, delta;
+ 
+-        if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
++        // BEGIN android-changed
++        if (elems.getKey(0).equals(other.elems.getKey(0)))   // guess forward
++        // END android-changed
+         {
+             start = 0;
+             end = orderingSize;
+@@ -1002,8 +1018,10 @@
+         for (int i = start; i != end; i += delta)
+         {
+             boolean              found = false;
+-            DERObjectIdentifier  oid = (DERObjectIdentifier)ordering.elementAt(i);
+-            String               value = (String)values.elementAt(i);
++            // BEGIN android-changed
++            DERObjectIdentifier  oid = elems.getKey(i);
++            String               value = elems.getValue(i);
++            // END android-changed
+ 
+             for (int j = 0; j < orderingSize; j++)
+             {
+@@ -1012,11 +1030,15 @@
+                     continue;
+                 }
+ 
+-                DERObjectIdentifier oOid = (DERObjectIdentifier)other.ordering.elementAt(j);
++                // BEGIN android-changed
++                DERObjectIdentifier oOid = other.elems.getKey(j);
++                // END android-changed
+ 
+                 if (oid.equals(oOid))
+                 {
+-                    String oValue = (String)other.values.elementAt(j);
++                    // BEGIN android-changed
++                    String oValue = other.elems.getValue(j);
++                    // END android-changed
+ 
+                     if (equivalentStrings(value, oValue))
+                     {
+@@ -1181,28 +1203,36 @@
+ 
+         StringBuffer ava = null;
+ 
+-        for (int i = 0; i < ordering.size(); i++)
++        // BEGIN android-changed
++        for (int i = 0; i < elems.size(); i++)
++        // END android-changed
+         {
+-            if (((Boolean)added.elementAt(i)).booleanValue())
++            if (elems.getAdded(i))
+             {
+                 ava.append('+');
+                 appendValue(ava, oidSymbols,
+-                    (DERObjectIdentifier)ordering.elementAt(i),
+-                    (String)values.elementAt(i));
++                    // BEGIN android-changed
++                    elems.getKey(i),
++                    elems.getValue(i));
++                    // END android-changed
+             }
+             else
+             {
+                 ava = new StringBuffer();
+                 appendValue(ava, oidSymbols,
+-                    (DERObjectIdentifier)ordering.elementAt(i),
+-                    (String)values.elementAt(i));
++                    // BEGIN android-changed
++                    elems.getKey(i),
++                    elems.getValue(i));
++                    // END android-changed
+                 components.addElement(ava);
+             }
+         }
+ 
+         if (reverse)
+         {
+-            for (int i = components.size() - 1; i >= 0; i--)
++            // BEGIN android-changed
++            for (int i = elems.size() - 1; i >= 0; i--)
++            // END android-changed
+             {
+                 if (first)
+                 {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509NameElementList.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509NameElementList.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509NameElementList.java	1970-01-01 00:00:00.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509NameElementList.java	2010-06-25 23:17:26.000000000 +0000
+@@ -0,0 +1,206 @@
++package org.bouncycastle.asn1.x509;
++
++import java.util.ArrayList;
++import java.util.BitSet;
++import org.bouncycastle.asn1.DERObjectIdentifier;
++
++// BEGIN android-note
++// This class was extracted from X509Name as a way to keep the element
++// list in a more controlled fashion.
++// END android-note
++
++/**
++ * List of elements of an X509 name. Each element has a key, a value, and
++ * an "added" flag.
++ */
++public class X509NameElementList {
++    /** null-ok; key #0 */
++    private DERObjectIdentifier key0;
++    
++    /** null-ok; key #1 */
++    private DERObjectIdentifier key1;
++
++    /** null-ok; key #2 */
++    private DERObjectIdentifier key2;
++
++    /** null-ok; key #3 */
++    private DERObjectIdentifier key3;
++    
++    /** null-ok; value #0 */
++    private String value0;
++    
++    /** null-ok; value #1 */
++    private String value1;
++
++    /** null-ok; value #2 */
++    private String value2;
++
++    /** null-ok; value #3 */
++    private String value3;
++    
++    /**
++     * null-ok; array of additional keys and values, alternating
++     * key then value, etc. 
++     */
++    private ArrayList<Object> rest;
++
++    /** bit vector for all the "added" bits */
++    private BitSet added = new BitSet();
++
++    /** &gt;= 0; number of elements in the list */
++    private int size;
++
++    // Note: Default public constructor.
++    
++    /**
++     * Adds an element. The "added" flag is set to false for the element.
++     * 
++     * @param key non-null; the key
++     * @param value non-null; the value
++     */
++    public void add(DERObjectIdentifier key, String value) {
++        add(key, value, false);
++    }
++
++    /**
++     * Adds an element.
++     * 
++     * @param key non-null; the key
++     * @param value non-null; the value
++     * @param added the added bit
++     */
++    public void add(DERObjectIdentifier key, String value, boolean added) {
++        if (key == null) {
++            throw new NullPointerException("key == null");
++        }
++
++        if (value == null) {
++            throw new NullPointerException("value == null");
++        }
++
++        int sz = size;
++
++        switch (sz) {
++            case 0: {
++                key0 = key;
++                value0 = value;
++                break;
++            }
++            case 1: {
++                key1 = key;
++                value1 = value;
++                break;
++            }
++            case 2: {
++                key2 = key;
++                value2 = value;
++                break;
++            }
++            case 3: {
++                key3 = key;
++                value3 = value;
++                break;
++            }
++            case 4: {
++                // Do initial allocation of rest.
++                rest = new ArrayList<Object>();
++                // Fall through...
++            }
++            default: {
++                rest.add(key);
++                rest.add(value);
++                break;
++            }
++        }
++
++        if (added) {
++            this.added.set(sz);
++        }
++        
++        size = sz + 1;
++    }
++
++    /**
++     * Sets the "added" flag on the most recently added element.
++     */
++    public void setLastAddedFlag() {
++        added.set(size - 1);
++    }
++
++    /**
++     * Gets the number of elements in this instance.
++     */
++    public int size() {
++        return size;
++    }
++    
++    /**
++     * Gets the nth key.
++     * 
++     * @param n index
++     * @return non-null; the nth key
++     */
++    public DERObjectIdentifier getKey(int n) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++
++        switch (n) {
++            case 0: return key0;
++            case 1: return key1;
++            case 2: return key2;
++            case 3: return key3;
++            default: return (DERObjectIdentifier) rest.get((n - 4) * 2);
++        }
++    }
++
++    /**
++     * Gets the nth value.
++     * 
++     * @param n index
++     * @return non-null; the nth value
++     */
++    public String getValue(int n) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++
++        switch (n) {
++            case 0: return value0;
++            case 1: return value1;
++            case 2: return value2;
++            case 3: return value3;
++            default: return (String) rest.get(((n - 4) * 2) + 1);
++        }
++    }
++
++    /**
++     * Gets the nth added flag bit.
++     * 
++     * @param n index
++     * @return the nth added flag bit
++     */
++    public boolean getAdded(int n) {
++        if ((n < 0) || (n >= size)) {
++            throw new IndexOutOfBoundsException(Integer.toString(n));
++        }
++
++        return added.get(n);
++    }
++
++    /**
++     * Constructs and returns a new instance which consists of the
++     * elements of this one in reverse order
++     * 
++     * @return non-null; the reversed instance
++     */
++    public X509NameElementList reverse() {
++        X509NameElementList result = new X509NameElementList();
++            
++        for (int i = size - 1; i >= 0; i--) {
++            result.add(getKey(i), getValue(i), getAdded(i));
++        }
++
++        return result;
++    }
++}
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509NameTokenizer.java bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509NameTokenizer.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/asn1/x509/X509NameTokenizer.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/asn1/x509/X509NameTokenizer.java	2010-06-25 23:17:26.000000000 +0000
+@@ -58,6 +58,17 @@
+                 }
+                 else
+                 {
++                    // BEGIN android-added
++                    // copied from a newer version of BouncyCastle
++                    if (c == '#' && buf.charAt(buf.length() - 1) == '=')
++                    {
++                        buf.append('\\');
++                    }
++                    else if (c == '+' && seperator != '+')
++                    {
++                        buf.append('\\');
++                    }
++                    // END android-added
+                     buf.append(c);
+                 }
+                 escaped = false;
+@@ -96,4 +107,4 @@
+         index = end;
+         return buf.toString().trim();
+     }
+-}
++}
+\ No newline at end of file
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/PBEParametersGenerator.java bcprov-jdk16-145/org/bouncycastle/crypto/PBEParametersGenerator.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/PBEParametersGenerator.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/PBEParametersGenerator.java	2010-06-25 23:17:26.000000000 +0000
+@@ -136,7 +136,8 @@
+     public static byte[] PKCS12PasswordToBytes(
+         char[]  password)
+     {
+-        if (password.length > 0)
++        // BEGIN android-changed
++        if (password != null && password.length > 0)
+         {
+                                        // +1 for extra 2 pad bytes.
+             byte[]  bytes = new byte[(password.length + 1) * 2];
+@@ -153,5 +154,6 @@
+         {
+             return new byte[0];
+         }
++        // END android-changed
+     }
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/digests/OpenSSLDigest.java bcprov-jdk16-145/org/bouncycastle/crypto/digests/OpenSSLDigest.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/digests/OpenSSLDigest.java	1970-01-01 00:00:00.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/digests/OpenSSLDigest.java	2010-06-25 23:17:26.000000000 +0000
+@@ -0,0 +1,120 @@
++/*
++ * Copyright (C) 2008 The Android Open Source Project
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++package org.bouncycastle.crypto.digests;
++
++import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
++import org.bouncycastle.crypto.ExtendedDigest;
++
++/**
++ * Implements the BouncyCastle Digest interface using OpenSSL's EVP API.
++ */
++public class OpenSSLDigest implements ExtendedDigest {
++
++    /**
++     * Holds the name of the hashing algorithm, e.g. "SHA-1";
++     */
++    private String algorithm;
++
++    /**
++     * Holds a pointer to the native message digest context.
++     */
++    private int ctx;
++
++    /**
++     * Holds a dummy buffer for writing single bytes to the digest.
++     */
++    private byte[] singleByte = new byte[1];
++
++    /**
++     * Creates a new OpenSSLMessageDigest instance for the given algorithm
++     * name.
++     *
++     * @param algorithm The name of the algorithm, e.g. "SHA1".
++     */
++    private OpenSSLDigest(String algorithm) {
++        this.algorithm = algorithm;
++
++        // We don't support MD2.
++        if ("MD2".equalsIgnoreCase(algorithm)) {
++            throw new RuntimeException(algorithm + " not supported");
++        }
++
++        ctx = NativeCrypto.EVP_new();
++        try {
++            NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
++        } catch (Exception ex) {
++            throw new RuntimeException(ex.getMessage() + " (" + algorithm + ")");
++        }
++    }
++
++    public int doFinal(byte[] out, int outOff) {
++        int i = NativeCrypto.EVP_DigestFinal(ctx, out, outOff);
++        reset();
++        return i;
++    }
++
++    public String getAlgorithmName() {
++        return algorithm;
++    }
++
++    public int getDigestSize() {
++        return NativeCrypto.EVP_DigestSize(ctx);
++    }
++
++    public int getByteLength() {
++        return NativeCrypto.EVP_DigestBlockSize(ctx);
++    }
++
++    public void reset() {
++        NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
++    }
++
++    public void update(byte in) {
++        singleByte[0] = in;
++        NativeCrypto.EVP_DigestUpdate(ctx, singleByte, 0, 1);
++    }
++
++    public void update(byte[] in, int inOff, int len) {
++        NativeCrypto.EVP_DigestUpdate(ctx, in, inOff, len);
++    }
++
++    @Override
++    protected void finalize() throws Throwable {
++        super.finalize();
++        NativeCrypto.EVP_free(ctx);
++    }
++
++    public static class MD5 extends OpenSSLDigest {
++        public MD5() { super("MD5"); }
++    }
++
++    public static class SHA1 extends OpenSSLDigest {
++        public SHA1() { super("SHA-1"); }
++    }
++
++    public static class SHA256 extends OpenSSLDigest {
++        public SHA256() { super("SHA-256"); }
++    }
++
++    public static class SHA384 extends OpenSSLDigest {
++        public SHA384() { super("SHA-384"); }
++    }
++
++    public static class SHA512 extends OpenSSLDigest {
++        public SHA512() { super("SHA-512"); }
++    }
++}
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/engines/RC2Engine.java bcprov-jdk16-145/org/bouncycastle/crypto/engines/RC2Engine.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/engines/RC2Engine.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/engines/RC2Engine.java	2010-06-25 23:17:26.000000000 +0000
+@@ -313,4 +313,4 @@
+         out[outOff + 6] = (byte)x76;
+         out[outOff + 7] = (byte)(x76 >> 8);
+     }
+-}
++}
+\ No newline at end of file
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/macs/HMac.java bcprov-jdk16-145/org/bouncycastle/crypto/macs/HMac.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/macs/HMac.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/macs/HMac.java	2010-06-25 23:17:26.000000000 +0000
+@@ -32,23 +32,23 @@
+     {
+         blockLengths = new Hashtable();
+         
+-        blockLengths.put("GOST3411", new Integer(32));
++        blockLengths.put("GOST3411", Integer.valueOf(32));
+         
+-        blockLengths.put("MD2", new Integer(16));
+-        blockLengths.put("MD4", new Integer(64));
+-        blockLengths.put("MD5", new Integer(64));
+-        
+-        blockLengths.put("RIPEMD128", new Integer(64));
+-        blockLengths.put("RIPEMD160", new Integer(64));
+-        
+-        blockLengths.put("SHA-1", new Integer(64));
+-        blockLengths.put("SHA-224", new Integer(64));
+-        blockLengths.put("SHA-256", new Integer(64));
+-        blockLengths.put("SHA-384", new Integer(128));
+-        blockLengths.put("SHA-512", new Integer(128));
++        blockLengths.put("MD2", Integer.valueOf(16));
++        blockLengths.put("MD4", Integer.valueOf(64));
++        blockLengths.put("MD5", Integer.valueOf(64));
++        
++        blockLengths.put("RIPEMD128", Integer.valueOf(64));
++        blockLengths.put("RIPEMD160", Integer.valueOf(64));
++        
++        blockLengths.put("SHA-1", Integer.valueOf(64));
++        blockLengths.put("SHA-224", Integer.valueOf(64));
++        blockLengths.put("SHA-256", Integer.valueOf(64));
++        blockLengths.put("SHA-384", Integer.valueOf(128));
++        blockLengths.put("SHA-512", Integer.valueOf(128));
+         
+-        blockLengths.put("Tiger", new Integer(64));
+-        blockLengths.put("Whirlpool", new Integer(64));
++        blockLengths.put("Tiger", Integer.valueOf(64));
++        blockLengths.put("Whirlpool", Integer.valueOf(64));
+     }
+     
+     private static int getByteLength(
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/signers/RSADigestSigner.java bcprov-jdk16-145/org/bouncycastle/crypto/signers/RSADigestSigner.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/signers/RSADigestSigner.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/signers/RSADigestSigner.java	2010-06-25 23:17:26.000000000 +0000
+@@ -46,8 +46,10 @@
+         oidMap.put("SHA-384", NISTObjectIdentifiers.id_sha384);
+         oidMap.put("SHA-512", NISTObjectIdentifiers.id_sha512);
+ 
+-        oidMap.put("MD2", PKCSObjectIdentifiers.md2);
+-        oidMap.put("MD4", PKCSObjectIdentifiers.md4);
++        // BEGIN android-removed
++        // oidMap.put("MD2", PKCSObjectIdentifiers.md2);
++        // oidMap.put("MD4", PKCSObjectIdentifiers.md4);
++        // END android-removed
+         oidMap.put("MD5", PKCSObjectIdentifiers.md5);
+     }
+ 
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/util/PrivateKeyFactory.java bcprov-jdk16-145/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/util/PrivateKeyFactory.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/util/PrivateKeyFactory.java	2010-06-25 23:17:26.000000000 +0000
+@@ -7,31 +7,39 @@
+ import org.bouncycastle.asn1.DERInteger;
+ import org.bouncycastle.asn1.DERObject;
+ import org.bouncycastle.asn1.DERObjectIdentifier;
+-import org.bouncycastle.asn1.nist.NISTNamedCurves;
+-import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.nist.NISTNamedCurves;
++// import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// END android-removed
+ import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+ import org.bouncycastle.asn1.pkcs.DHParameter;
+ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+ import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+ import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
+-import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
+-import org.bouncycastle.asn1.sec.SECNamedCurves;
+-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
++// import org.bouncycastle.asn1.sec.SECNamedCurves;
++// import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
++// END android-removed
+ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+ import org.bouncycastle.asn1.x509.DSAParameter;
+-import org.bouncycastle.asn1.x9.X962NamedCurves;
+-import org.bouncycastle.asn1.x9.X962Parameters;
+-import org.bouncycastle.asn1.x9.X9ECParameters;
+-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.x9.X962NamedCurves;
++// import org.bouncycastle.asn1.x9.X962Parameters;
++// import org.bouncycastle.asn1.x9.X9ECParameters;
++// import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
++// END android-removed
+ import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+ import org.bouncycastle.crypto.params.DHParameters;
+ import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+ import org.bouncycastle.crypto.params.DSAParameters;
+ import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+-import org.bouncycastle.crypto.params.ECDomainParameters;
+-import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+-import org.bouncycastle.crypto.params.ElGamalParameters;
+-import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.params.ECDomainParameters;
++// import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
++// import org.bouncycastle.crypto.params.ElGamalParameters;
++// import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
++// END android-removed
+ import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+ 
+ import java.io.IOException;
+@@ -113,75 +121,77 @@
+ 
+             return new DHPrivateKeyParameters(derX.getValue(), dhParams);
+         }
+-        else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+-        {
+-            ElGamalParameter    params = new ElGamalParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
+-            DERInteger          derX = (DERInteger)keyInfo.getPrivateKey();
+-
+-            return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(params.getP(), params.getG()));
+-        }
+-        else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa))
+-        {
+-            DERInteger derX = (DERInteger)keyInfo.getPrivateKey();
+-            DEREncodable de = keyInfo.getAlgorithmId().getParameters();
+-
+-            DSAParameters parameters = null;
+-            if (de != null)
+-            {
+-                DSAParameter params = DSAParameter.getInstance(de.getDERObject());
+-                parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
+-            }
+-
+-            return new DSAPrivateKeyParameters(derX.getValue(), parameters);
+-        }
+-        else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
+-        {
+-            X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
+-            ECDomainParameters  dParams = null;
+-            
+-            if (params.isNamedCurve())
+-            {
+-                DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
+-                X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
+-
+-                if (ecP == null)
+-                {
+-                    ecP = SECNamedCurves.getByOID(oid);
+-
+-                    if (ecP == null)
+-                    {
+-                        ecP = NISTNamedCurves.getByOID(oid);
+-
+-                        if (ecP == null)
+-                        {
+-                            ecP = TeleTrusTNamedCurves.getByOID(oid);
+-                        }
+-                    }
+-                }
+-
+-                dParams = new ECDomainParameters(
+-                                            ecP.getCurve(),
+-                                            ecP.getG(),
+-                                            ecP.getN(),
+-                                            ecP.getH(),
+-                                            ecP.getSeed());
+-            }
+-            else
+-            {
+-                X9ECParameters ecP = new X9ECParameters(
+-                            (ASN1Sequence)params.getParameters());
+-                dParams = new ECDomainParameters(
+-                                            ecP.getCurve(),
+-                                            ecP.getG(),
+-                                            ecP.getN(),
+-                                            ecP.getH(),
+-                                            ecP.getSeed());
+-            }
+-
+-            ECPrivateKeyStructure   ec = new ECPrivateKeyStructure((ASN1Sequence)keyInfo.getPrivateKey());
+-
+-            return new ECPrivateKeyParameters(ec.getKey(), dParams);
+-        }
++        // BEGIN android-removed
++        // else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
++        // {
++        //     ElGamalParameter    params = new ElGamalParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
++        //     DERInteger          derX = (DERInteger)keyInfo.getPrivateKey();
++        //
++        //     return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(params.getP(), params.getG()));
++        // }
++        // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa))
++        // {
++        //     DERInteger derX = (DERInteger)keyInfo.getPrivateKey();
++        //     DEREncodable de = keyInfo.getAlgorithmId().getParameters();
++        //
++        //     DSAParameters parameters = null;
++        //     if (de != null)
++        //     {
++        //         DSAParameter params = DSAParameter.getInstance(de.getDERObject());
++        //         parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
++        //     }
++        //
++        //     return new DSAPrivateKeyParameters(derX.getValue(), parameters);
++        // }
++        // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
++        // {
++        //     X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
++        //     ECDomainParameters  dParams = null;
++        //
++        //     if (params.isNamedCurve())
++        //     {
++        //         DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
++        //         X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
++        //
++        //         if (ecP == null)
++        //         {
++        //             ecP = SECNamedCurves.getByOID(oid);
++        //
++        //             if (ecP == null)
++        //             {
++        //                 ecP = NISTNamedCurves.getByOID(oid);
++        //
++        //                 if (ecP == null)
++        //                 {
++        //                     ecP = TeleTrusTNamedCurves.getByOID(oid);
++        //                 }
++        //             }
++        //         }
++        //
++        //         dParams = new ECDomainParameters(
++        //                                     ecP.getCurve(),
++        //                                     ecP.getG(),
++        //                                     ecP.getN(),
++        //                                     ecP.getH(),
++        //                                     ecP.getSeed());
++        //     }
++        //     else
++        //     {
++        //         X9ECParameters ecP = new X9ECParameters(
++        //                     (ASN1Sequence)params.getParameters());
++        //         dParams = new ECDomainParameters(
++        //                                     ecP.getCurve(),
++        //                                     ecP.getG(),
++        //                                     ecP.getN(),
++        //                                     ecP.getH(),
++        //                                     ecP.getSeed());
++        //     }
++        //
++        //     ECPrivateKeyStructure   ec = new ECPrivateKeyStructure((ASN1Sequence)keyInfo.getPrivateKey());
++        //
++        //     return new ECPrivateKeyParameters(ec.getKey(), dParams);
++        // }
++        // END android-removed
+         else
+         {
+             throw new RuntimeException("algorithm identifier in key not recognised");
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/crypto/util/PublicKeyFactory.java bcprov-jdk16-145/org/bouncycastle/crypto/util/PublicKeyFactory.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/crypto/util/PublicKeyFactory.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/crypto/util/PublicKeyFactory.java	2010-06-25 23:17:26.000000000 +0000
+@@ -10,32 +10,40 @@
+ import org.bouncycastle.asn1.DERObject;
+ import org.bouncycastle.asn1.DERObjectIdentifier;
+ import org.bouncycastle.asn1.DEROctetString;
+-import org.bouncycastle.asn1.nist.NISTNamedCurves;
+-import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.nist.NISTNamedCurves;
++// import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// END android-removed
+ import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+ import org.bouncycastle.asn1.pkcs.DHParameter;
+ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+-import org.bouncycastle.asn1.sec.SECNamedCurves;
+-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.sec.SECNamedCurves;
++// import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
++// END android-removed
+ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+ import org.bouncycastle.asn1.x509.DSAParameter;
+ import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
+ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+ import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+-import org.bouncycastle.asn1.x9.X962NamedCurves;
+-import org.bouncycastle.asn1.x9.X962Parameters;
+-import org.bouncycastle.asn1.x9.X9ECParameters;
+-import org.bouncycastle.asn1.x9.X9ECPoint;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.x9.X962NamedCurves;
++// import org.bouncycastle.asn1.x9.X962Parameters;
++// import org.bouncycastle.asn1.x9.X9ECParameters;
++// import org.bouncycastle.asn1.x9.X9ECPoint;
++// END android-removed
+ import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+ import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+ import org.bouncycastle.crypto.params.DHParameters;
+ import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+ import org.bouncycastle.crypto.params.DSAParameters;
+ import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+-import org.bouncycastle.crypto.params.ECDomainParameters;
+-import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+-import org.bouncycastle.crypto.params.ElGamalParameters;
+-import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.params.ECDomainParameters;
++// import org.bouncycastle.crypto.params.ECPublicKeyParameters;
++// import org.bouncycastle.crypto.params.ElGamalParameters;
++// import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
++// END android-removed
+ import org.bouncycastle.crypto.params.RSAKeyParameters;
+ 
+ import java.io.IOException;
+@@ -112,13 +120,15 @@
+ 
+             return new DHPublicKeyParameters(derY.getValue(), dhParams);
+         }
+-        else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
+-        {
+-            ElGamalParameter    params = new ElGamalParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
+-            DERInteger          derY = (DERInteger)keyInfo.getPublicKey();
+-
+-            return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters(params.getP(), params.getG()));
+-        }
++        // BEGIN android-removed
++        // else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
++        // {
++        //     ElGamalParameter    params = new ElGamalParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
++        //     DERInteger          derY = (DERInteger)keyInfo.getPublicKey();
++        //
++        //     return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters(params.getP(), params.getG()));
++        // }
++        // END android-removed
+         else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa)
+                  || algId.getObjectId().equals(OIWObjectIdentifiers.dsaWithSHA1))
+         {
+@@ -134,58 +144,60 @@
+ 
+             return new DSAPublicKeyParameters(derY.getValue(), parameters);
+         }
+-        else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
+-        {
+-            X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
+-            ECDomainParameters  dParams = null;
+-            
+-            if (params.isNamedCurve())
+-            {
+-                DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
+-                X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
+-
+-                if (ecP == null)
+-                {
+-                    ecP = SECNamedCurves.getByOID(oid);
+-
+-                    if (ecP == null)
+-                    {
+-                        ecP = NISTNamedCurves.getByOID(oid);
+-
+-                        if (ecP == null)
+-                        {
+-                            ecP = TeleTrusTNamedCurves.getByOID(oid);
+-                        }
+-                    }
+-                }
+-
+-                dParams = new ECDomainParameters(
+-                                            ecP.getCurve(),
+-                                            ecP.getG(),
+-                                            ecP.getN(),
+-                                            ecP.getH(),
+-                                            ecP.getSeed());
+-            }
+-            else
+-            {
+-                X9ECParameters ecP = new X9ECParameters(
+-                            (ASN1Sequence)params.getParameters());
+-                dParams = new ECDomainParameters(
+-                                            ecP.getCurve(),
+-                                            ecP.getG(),
+-                                            ecP.getN(),
+-                                            ecP.getH(),
+-                                            ecP.getSeed());
+-            }
+-
+-            DERBitString    bits = keyInfo.getPublicKeyData();
+-            byte[]          data = bits.getBytes();
+-            ASN1OctetString key = new DEROctetString(data);
+-
+-            X9ECPoint       derQ = new X9ECPoint(dParams.getCurve(), key);
+-            
+-            return new ECPublicKeyParameters(derQ.getPoint(), dParams);
+-        }
++        // BEGIN android-removed
++        // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
++        // {
++        //     X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
++        //     ECDomainParameters  dParams = null;
++        //
++        //     if (params.isNamedCurve())
++        //     {
++        //         DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
++        //         X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
++        //
++        //         if (ecP == null)
++        //         {
++        //             ecP = SECNamedCurves.getByOID(oid);
++        //
++        //             if (ecP == null)
++        //             {
++        //                 ecP = NISTNamedCurves.getByOID(oid);
++        //
++        //                 if (ecP == null)
++        //                 {
++        //                     ecP = TeleTrusTNamedCurves.getByOID(oid);
++        //                 }
++        //             }
++        //         }
++        //
++        //         dParams = new ECDomainParameters(
++        //                                     ecP.getCurve(),
++        //                                     ecP.getG(),
++        //                                     ecP.getN(),
++        //                                     ecP.getH(),
++        //                                     ecP.getSeed());
++        //     }
++        //     else
++        //     {
++        //         X9ECParameters ecP = new X9ECParameters(
++        //                     (ASN1Sequence)params.getParameters());
++        //         dParams = new ECDomainParameters(
++        //                                     ecP.getCurve(),
++        //                                     ecP.getG(),
++        //                                     ecP.getN(),
++        //                                     ecP.getH(),
++        //                                     ecP.getSeed());
++        //     }
++        //
++        //     DERBitString    bits = keyInfo.getPublicKeyData();
++        //     byte[]          data = bits.getBytes();
++        //     ASN1OctetString key = new DEROctetString(data);
++        //
++        //     X9ECPoint       derQ = new X9ECPoint(dParams.getCurve(), key);
++        //
++        //     return new ECPublicKeyParameters(derQ.getPoint(), dParams);
++        // }
++        // END android-removed
+         else
+         {
+             throw new RuntimeException("algorithm identifier in key not recognised");
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/PKCS10CertificationRequest.java bcprov-jdk16-145/org/bouncycastle/jce/PKCS10CertificationRequest.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/PKCS10CertificationRequest.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/PKCS10CertificationRequest.java	2010-06-25 23:17:26.000000000 +0000
+@@ -78,8 +78,11 @@
+ 
+     static
+     {
+-        algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
+-        algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
++        // BEGIN android-removed
++        // Dropping MD2
++        // algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
++        // algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
++        // END android-removed
+         algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
+         algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
+         algorithms.put("RSAWITHMD5", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
+@@ -129,7 +132,10 @@
+         oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+         
+         oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+-        oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
++        // BEGIN android-removed
++        // Dropping MD2
++        // oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
++        // END android-removed
+         oids.put(new DERObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+         oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
+         oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
+@@ -168,19 +174,29 @@
+         //
+         // explicit params
+         //
+-        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA1WITHRSAANDMGF1", creatPSSParams(sha1AlgId, 20));
+ 
+-        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA224WITHRSAANDMGF1", creatPSSParams(sha224AlgId, 28));
+ 
+-        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA256WITHRSAANDMGF1", creatPSSParams(sha256AlgId, 32));
+ 
+-        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA384WITHRSAANDMGF1", creatPSSParams(sha384AlgId, 48));
+ 
+-        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA512WITHRSAANDMGF1", creatPSSParams(sha512AlgId, 64));
+     }
+ 
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/BouncyCastleProvider.java bcprov-jdk16-145/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/BouncyCastleProvider.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/BouncyCastleProvider.java	2010-06-25 23:17:26.000000000 +0000
+@@ -53,7 +53,12 @@
+     private static final String SYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jce.provider.symmetric.";
+     private static final String[] SYMMETRIC_CIPHERS =
+     {
+-        "AES", "Camellia", "CAST5", "Grainv1", "Grain128", "IDEA", "Noekeon", "SEED"
++        // BEGIN android-removed
++        // "AES", "Camellia", "CAST5", "Grainv1", "Grain128", "IDEA", "Noekeon", "SEED"
++        // END android-removed
++        // BEGIN android-added
++        "AES",
++        // END android-added
+     };
+ 
+     /*
+@@ -62,7 +67,9 @@
+     private static final String ASYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jce.provider.asymmetric.";
+     private static final String[] ASYMMETRIC_CIPHERS =
+     {
+-        "EC"
++        // BEGIN android-removed
++        // "EC"
++        // END android-removed
+     };
+ 
+     /**
+@@ -89,26 +96,28 @@
+         loadAlgorithms(SYMMETRIC_CIPHER_PACKAGE, SYMMETRIC_CIPHERS);
+         loadAlgorithms(ASYMMETRIC_CIPHER_PACKAGE, ASYMMETRIC_CIPHERS);
+ 
+-        //
+-        // X509Store
+-        //
+-        put("X509Store.CERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertCollection");
+-        put("X509Store.ATTRIBUTECERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreAttrCertCollection");
+-        put("X509Store.CRL/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCRLCollection");
+-        put("X509Store.CERTIFICATEPAIR/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertPairCollection");
+-
+-        put("X509Store.CERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCerts");
+-        put("X509Store.CRL/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCRLs");
+-        put("X509Store.ATTRIBUTECERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPAttrCerts");
+-        put("X509Store.CERTIFICATEPAIR/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCertPairs");
+-        
+-        //
+-        // X509StreamParser
+-        //
+-        put("X509StreamParser.CERTIFICATE", "org.bouncycastle.jce.provider.X509CertParser");
+-        put("X509StreamParser.ATTRIBUTECERTIFICATE", "org.bouncycastle.jce.provider.X509AttrCertParser");
+-        put("X509StreamParser.CRL", "org.bouncycastle.jce.provider.X509CRLParser");
+-        put("X509StreamParser.CERTIFICATEPAIR", "org.bouncycastle.jce.provider.X509CertPairParser");
++        // BEGIN android-removed
++        // //
++        // // X509Store
++        // //
++        // put("X509Store.CERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertCollection");
++        // put("X509Store.ATTRIBUTECERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreAttrCertCollection");
++        // put("X509Store.CRL/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCRLCollection");
++        // put("X509Store.CERTIFICATEPAIR/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertPairCollection");
++        //
++        // put("X509Store.CERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCerts");
++        // put("X509Store.CRL/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCRLs");
++        // put("X509Store.ATTRIBUTECERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPAttrCerts");
++        // put("X509Store.CERTIFICATEPAIR/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCertPairs");
++        //
++        // //
++        // // X509StreamParser
++        // //
++        // put("X509StreamParser.CERTIFICATE", "org.bouncycastle.jce.provider.X509CertParser");
++        // put("X509StreamParser.ATTRIBUTECERTIFICATE", "org.bouncycastle.jce.provider.X509AttrCertParser");
++        // put("X509StreamParser.CRL", "org.bouncycastle.jce.provider.X509CRLParser");
++        // put("X509StreamParser.CERTIFICATEPAIR", "org.bouncycastle.jce.provider.X509CertPairParser");
++        // END android-removed
+ 
+ 
+         //
+@@ -117,14 +126,24 @@
+         put("KeyStore.BKS", "org.bouncycastle.jce.provider.JDKKeyStore");
+         put("KeyStore.BouncyCastle", "org.bouncycastle.jce.provider.JDKKeyStore$BouncyCastleStore");
+         put("KeyStore.PKCS12", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
+-        put("KeyStore.BCPKCS12", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
+-        put("KeyStore.PKCS12-DEF", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+-
+-        put("KeyStore.PKCS12-3DES-40RC2", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
+-        put("KeyStore.PKCS12-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore3DES");
+-
+-        put("KeyStore.PKCS12-DEF-3DES-40RC2", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+-        put("KeyStore.PKCS12-DEF-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore3DES");
++        // BEGIN android-changed
++        put("Alg.Alias.KeyStore.BCPKCS12", "PKCS12");
++        // END android-changed
++        // BEGIN android-removed
++        // put("KeyStore.PKCS12-DEF", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
++        // END android-removed
++
++        // BEGIN android-changed
++        put("Alg.Alias.KeyStore.PKCS12-3DES-40RC2", "PKCS12");
++        // END android-changed
++        // BEGIN android-removed
++        // put("KeyStore.PKCS12-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore3DES");
++        // END android-removed
++
++        // BEGIN android-removed
++        // put("KeyStore.PKCS12-DEF-3DES-40RC2", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
++        // put("KeyStore.PKCS12-DEF-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore3DES");
++        // END android-removed
+ 
+         put("Alg.Alias.KeyStore.UBER", "BouncyCastle");
+         put("Alg.Alias.KeyStore.BOUNCYCASTLE", "BouncyCastle");
+@@ -141,105 +160,122 @@
+         //
+         put("AlgorithmParameterGenerator.DH", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DH");
+         put("AlgorithmParameterGenerator.DSA", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DSA");
+-        put("AlgorithmParameterGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$GOST3410");
+-        put("AlgorithmParameterGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$ElGamal");
+-        put("AlgorithmParameterGenerator.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+-        put("AlgorithmParameterGenerator.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+-        put("AlgorithmParameterGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+-        put("AlgorithmParameterGenerator." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+-        put("AlgorithmParameterGenerator.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
+-        put("AlgorithmParameterGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
++        // BEGIN android-removed
++        // put("AlgorithmParameterGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$GOST3410");
++        // put("AlgorithmParameterGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$ElGamal");
++        // put("AlgorithmParameterGenerator.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
++        // put("AlgorithmParameterGenerator.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
++        // put("AlgorithmParameterGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
++        // put("AlgorithmParameterGenerator." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
++        // put("AlgorithmParameterGenerator.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
++        // put("AlgorithmParameterGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
++        // END android-removed
+ 
+         put("Alg.Alias.AlgorithmParameterGenerator.DIFFIEHELLMAN", "DH");
+-        put("Alg.Alias.AlgorithmParameterGenerator.GOST-3410", "GOST3410");
++        // BEGIN android-removed
++        // put("Alg.Alias.AlgorithmParameterGenerator.GOST-3410", "GOST3410");
++        // END android-removed
+         //
+         // algorithm parameters
+         //
+         put("AlgorithmParameters.OAEP", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$OAEP");
+-        put("AlgorithmParameters.PSS", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PSS");
++        // BEGIN android-removed
++        // put("AlgorithmParameters.PSS", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PSS");
++        // END android-removed
+         put("AlgorithmParameters.DH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$DH");
+         put("Alg.Alias.AlgorithmParameters.DIFFIEHELLMAN", "DH");
+         put("AlgorithmParameters.DSA", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$DSA");
+-        put("AlgorithmParameters.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$ElGamal");
+-        put("AlgorithmParameters.IES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IES");
++        // BEGIN android-removed
++        // put("AlgorithmParameters.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$ElGamal");
++        // put("AlgorithmParameters.IES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IES");
++        // END android-removed
+         put("AlgorithmParameters.PKCS12PBE", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PKCS12PBE");
+-        put("AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PBKDF2");
+-
+-        put("AlgorithmParameters.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$GOST3410");
+-        put("Alg.Alias.AlgorithmParameters.GOST-3410", "GOST3410");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC2", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC4", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2-CBC", "PKCS12PBE");
++        // BEGIN android-changed
++        // redundant with below
++        // put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
++        // END android-changed
++        // BEGIN android-removed
++        // put("AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PBKDF2");
++        //
++        // put("AlgorithmParameters.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$GOST3410");
++        // put("Alg.Alias.AlgorithmParameters.GOST-3410", "GOST3410");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC2", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC4", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2-CBC", "PKCS12PBE");
++        // END android-removed
+         put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC2-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC4", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC2-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC4", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.1", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.2", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.3", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.4", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.5", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.6", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWithSHAAnd3KeyTripleDES", "PKCS12PBE");
+-
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
++        // BEGIN android-removed
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC2-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC4", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC2-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC4", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.1", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.2", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.3", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.4", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.5", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.6", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWithSHAAnd3KeyTripleDES", "PKCS12PBE");
++        //
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
++        // END android-removed
+ 
+         put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP");
+         
+-        put("Alg.Alias.AlgorithmParameters.RSAPSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.RSASSA-PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA224withRSA/PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA256withRSA/PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA384withRSA/PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA512withRSA/PSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA224WITHRSAANDMGF1", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA256WITHRSAANDMGF1", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA384WITHRSAANDMGF1", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.SHA512WITHRSAANDMGF1", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.RAWRSAPSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.NONEWITHRSAPSS", "PSS");
+-        put("Alg.Alias.AlgorithmParameters.NONEWITHRSASSA-PSS", "PSS");
+-        
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
+-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE");
+-
+-        put("AlgorithmParameters.SHA1WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+-        put("AlgorithmParameters.SHA224WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+-        put("AlgorithmParameters.SHA256WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+-        put("AlgorithmParameters.SHA384WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+-        put("AlgorithmParameters.SHA512WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // BEGIN android-removed
++        // put("Alg.Alias.AlgorithmParameters.RSAPSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.RSASSA-PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA224withRSA/PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA256withRSA/PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA384withRSA/PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA512withRSA/PSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA224WITHRSAANDMGF1", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA256WITHRSAANDMGF1", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA384WITHRSAANDMGF1", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.SHA512WITHRSAANDMGF1", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.RAWRSAPSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.NONEWITHRSAPSS", "PSS");
++        // put("Alg.Alias.AlgorithmParameters.NONEWITHRSASSA-PSS", "PSS");
++        //
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
++        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE");
++        //
++        // put("AlgorithmParameters.SHA1WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // put("AlgorithmParameters.SHA224WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // put("AlgorithmParameters.SHA256WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // put("AlgorithmParameters.SHA384WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // put("AlgorithmParameters.SHA512WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
++        // END android-removed
+         
+         //
+         // key agreement
+@@ -252,134 +288,166 @@
+         //
+         put("Cipher.DES", "org.bouncycastle.jce.provider.JCEBlockCipher$DES");
+         put("Cipher.DESEDE", "org.bouncycastle.jce.provider.JCEBlockCipher$DESede");
+-        put("Cipher." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESedeCBC");
+-        put("Cipher." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESCBC");
++        // BEGIN android-removed
++        // put("Cipher." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESedeCBC");
++        // put("Cipher." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESCBC");
++        // END android-removed
+         put("Cipher.DESEDEWRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$DESEDEWrap");
+-        put("Cipher." + PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "org.bouncycastle.jce.provider.WrapCipherSpi$DESEDEWrap");
+-        put("Cipher.SKIPJACK", "org.bouncycastle.jce.provider.JCEBlockCipher$Skipjack");
+-        put("Cipher.BLOWFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Blowfish");
+-        put("Cipher.1.3.6.1.4.1.3029.1.2", "org.bouncycastle.jce.provider.JCEBlockCipher$BlowfishCBC");
+-        put("Cipher.TWOFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Twofish");
+-        put("Cipher.RC2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2");
+-        put("Cipher.RC2WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RC2Wrap");
+-        put("Cipher.1.2.840.113549.1.9.16.3.7", "org.bouncycastle.jce.provider.WrapCipherSpi$RC2Wrap");
+-        put("Cipher.ARC4", "org.bouncycastle.jce.provider.JCEStreamCipher$RC4");
+-        put("Alg.Alias.Cipher.1.2.840.113549.3.4", "ARC4");
+-        put("Alg.Alias.Cipher.ARCFOUR", "ARC4");
+-        put("Alg.Alias.Cipher.RC4", "ARC4");
+-        put("Cipher.SALSA20", "org.bouncycastle.jce.provider.JCEStreamCipher$Salsa20");
+-        put("Cipher.HC128", "org.bouncycastle.jce.provider.JCEStreamCipher$HC128");
+-        put("Cipher.HC256", "org.bouncycastle.jce.provider.JCEStreamCipher$HC256");
+-        put("Cipher.VMPC", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPC");
+-        put("Cipher.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPCKSA3");
+-        put("Cipher.RC5", "org.bouncycastle.jce.provider.JCEBlockCipher$RC5");
+-        put("Cipher.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2CBC");
+-        put("Alg.Alias.Cipher.RC5-32", "RC5");
+-        put("Cipher.RC5-64", "org.bouncycastle.jce.provider.JCEBlockCipher$RC564");
+-        put("Cipher.RC6", "org.bouncycastle.jce.provider.JCEBlockCipher$RC6");
+-        put("Cipher.RIJNDAEL", "org.bouncycastle.jce.provider.JCEBlockCipher$Rijndael");
+-        put("Cipher.DESEDERFC3211WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RFC3211DESedeWrap");
+-        put("Cipher.SERPENT", "org.bouncycastle.jce.provider.JCEBlockCipher$Serpent");
+-
+-
+-        put("Cipher.CAST6", "org.bouncycastle.jce.provider.JCEBlockCipher$CAST6");
++        // BEGIN android-changed
++        put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWRAP");
++        // END android-changed
++        // BEGIN android-removed
++        // put("Cipher.SKIPJACK", "org.bouncycastle.jce.provider.JCEBlockCipher$Skipjack");
++        // put("Cipher.BLOWFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Blowfish");
++        // put("Cipher.1.3.6.1.4.1.3029.1.2", "org.bouncycastle.jce.provider.JCEBlockCipher$BlowfishCBC");
++        // put("Cipher.TWOFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Twofish");
++        // put("Cipher.RC2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2");
++        // put("Cipher.RC2WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RC2Wrap");
++        // put("Cipher.1.2.840.113549.1.9.16.3.7", "org.bouncycastle.jce.provider.WrapCipherSpi$RC2Wrap");
++        // put("Cipher.ARC4", "org.bouncycastle.jce.provider.JCEStreamCipher$RC4");
++        // put("Alg.Alias.Cipher.1.2.840.113549.3.4", "ARC4");
++        // put("Alg.Alias.Cipher.ARCFOUR", "ARC4");
++        // put("Alg.Alias.Cipher.RC4", "ARC4");
++        // put("Cipher.SALSA20", "org.bouncycastle.jce.provider.JCEStreamCipher$Salsa20");
++        // put("Cipher.HC128", "org.bouncycastle.jce.provider.JCEStreamCipher$HC128");
++        // put("Cipher.HC256", "org.bouncycastle.jce.provider.JCEStreamCipher$HC256");
++        // put("Cipher.VMPC", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPC");
++        // put("Cipher.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPCKSA3");
++        // put("Cipher.RC5", "org.bouncycastle.jce.provider.JCEBlockCipher$RC5");
++        // put("Cipher.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2CBC");
++        // put("Alg.Alias.Cipher.RC5-32", "RC5");
++        // put("Cipher.RC5-64", "org.bouncycastle.jce.provider.JCEBlockCipher$RC564");
++        // put("Cipher.RC6", "org.bouncycastle.jce.provider.JCEBlockCipher$RC6");
++        // put("Cipher.RIJNDAEL", "org.bouncycastle.jce.provider.JCEBlockCipher$Rijndael");
++        // put("Cipher.DESEDERFC3211WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RFC3211DESedeWrap");
++        // put("Cipher.SERPENT", "org.bouncycastle.jce.provider.JCEBlockCipher$Serpent");
++        // END android-removed
++
++
++        // BEGIN android-removed
++        // put("Cipher.CAST6", "org.bouncycastle.jce.provider.JCEBlockCipher$CAST6");
++        // END android-removed
+         put("Alg.Alias.Cipher.PBEWithSHAAnd3KeyTripleDES",  "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+         
+-        put("Cipher.GOST28147", "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147");
+-        put("Alg.Alias.Cipher.GOST", "GOST28147");
+-        put("Alg.Alias.Cipher.GOST-28147", "GOST28147");
+-        put("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147cbc");
+-
+-        put("Cipher.TEA", "org.bouncycastle.jce.provider.JCEBlockCipher$TEA");
+-        put("Cipher.XTEA", "org.bouncycastle.jce.provider.JCEBlockCipher$XTEA");
++        // BEGIN android-removed
++        // put("Cipher.GOST28147", "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147");
++        // put("Alg.Alias.Cipher.GOST", "GOST28147");
++        // put("Alg.Alias.Cipher.GOST-28147", "GOST28147");
++        // put("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147cbc");
++        //
++        // put("Cipher.TEA", "org.bouncycastle.jce.provider.JCEBlockCipher$TEA");
++        // put("Cipher.XTEA", "org.bouncycastle.jce.provider.JCEBlockCipher$XTEA");
++        // END android-removed
+ 
+         put("Cipher.RSA", "org.bouncycastle.jce.provider.JCERSACipher$NoPadding");
+-        put("Cipher.RSA/RAW", "org.bouncycastle.jce.provider.JCERSACipher$NoPadding");
+-        put("Cipher.RSA/PKCS1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+-        put("Cipher.1.2.840.113549.1.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+-        put("Cipher.2.5.8.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+-        put("Cipher.RSA/1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PrivateOnly");
+-        put("Cipher.RSA/2", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PublicOnly");
+-        put("Cipher.RSA/OAEP", "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
+-        put("Cipher." + PKCSObjectIdentifiers.id_RSAES_OAEP, "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
+-        put("Cipher.RSA/ISO9796-1", "org.bouncycastle.jce.provider.JCERSACipher$ISO9796d1Padding");
+-
+-        put("Cipher.ECIES", "org.bouncycastle.jce.provider.JCEIESCipher$ECIES");
+-        put("Cipher.BrokenECIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenECIES");
+-        put("Cipher.IES", "org.bouncycastle.jce.provider.JCEIESCipher$IES");
+-        put("Cipher.BrokenIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenIES");
+-        put("Cipher.ELGAMAL", "org.bouncycastle.jce.provider.JCEElGamalCipher$NoPadding");
+-        put("Cipher.ELGAMAL/PKCS1", "org.bouncycastle.jce.provider.JCEElGamalCipher$PKCS1v1_5Padding");
++        // BEGIN android-changed
++        put("Alg.Alias.Cipher.RSA/RAW", "RSA");
++        // END android-changed
++        // BEGIN android-removed
++        // put("Cipher.RSA/PKCS1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
++        // put("Cipher.1.2.840.113549.1.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
++        // put("Cipher.2.5.8.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
++        // put("Cipher.RSA/1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PrivateOnly");
++        // put("Cipher.RSA/2", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PublicOnly");
++        // put("Cipher.RSA/OAEP", "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
++        // put("Cipher." + PKCSObjectIdentifiers.id_RSAES_OAEP, "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
++        // put("Cipher.RSA/ISO9796-1", "org.bouncycastle.jce.provider.JCERSACipher$ISO9796d1Padding");
++        // END android-removed
++
++        // BEGIN android-removed
++        // put("Cipher.ECIES", "org.bouncycastle.jce.provider.JCEIESCipher$ECIES");
++        // put("Cipher.BrokenECIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenECIES");
++        // put("Cipher.IES", "org.bouncycastle.jce.provider.JCEIESCipher$IES");
++        // put("Cipher.BrokenIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenIES");
++        // put("Cipher.ELGAMAL", "org.bouncycastle.jce.provider.JCEElGamalCipher$NoPadding");
++        // put("Cipher.ELGAMAL/PKCS1", "org.bouncycastle.jce.provider.JCEElGamalCipher$PKCS1v1_5Padding");
++        // END android-removed
+ 
+         put("Alg.Alias.Cipher.RSA//RAW", "RSA");
+         put("Alg.Alias.Cipher.RSA//NOPADDING", "RSA");
+-        put("Alg.Alias.Cipher.RSA//PKCS1PADDING", "RSA/PKCS1");
+-        put("Alg.Alias.Cipher.RSA//OAEPPADDING", "RSA/OAEP");
+-        put("Alg.Alias.Cipher.RSA//ISO9796-1PADDING", "RSA/ISO9796-1");
+-        
+-        put("Alg.Alias.Cipher.ELGAMAL/ECB/PKCS1PADDING", "ELGAMAL/PKCS1");
+-        put("Alg.Alias.Cipher.ELGAMAL/NONE/PKCS1PADDING", "ELGAMAL/PKCS1");
+-        put("Alg.Alias.Cipher.ELGAMAL/NONE/NOPADDING", "ELGAMAL");
++        // BEGIN android-removed
++        // put("Alg.Alias.Cipher.RSA//PKCS1PADDING", "RSA/PKCS1");
++        // put("Alg.Alias.Cipher.RSA//OAEPPADDING", "RSA/OAEP");
++        // put("Alg.Alias.Cipher.RSA//ISO9796-1PADDING", "RSA/ISO9796-1");
++        // END android-removed
++        
++        // BEGIN android-removed
++        // put("Alg.Alias.Cipher.ELGAMAL/ECB/PKCS1PADDING", "ELGAMAL/PKCS1");
++        // put("Alg.Alias.Cipher.ELGAMAL/NONE/PKCS1PADDING", "ELGAMAL/PKCS1");
++        // put("Alg.Alias.Cipher.ELGAMAL/NONE/NOPADDING", "ELGAMAL");
++        // END android-removed
+ 
+         put("Cipher.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndDES");
+-        put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES");
+-        put("Cipher.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndRC2");
+-        put("Cipher.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndDES");
+-        put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES");
+-        put("Cipher.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndRC2");
++        // BEGIN android-removed
++        // put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES");
++        // put("Cipher.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndRC2");
++        // put("Cipher.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndDES");
++        // put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES");
++        // put("Cipher.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndRC2");
++        // END android-removed
+         put("Cipher.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES3Key");
+-        put("Cipher.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES3Key");
+-        put("Cipher.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndDES3Key");
+-        put("Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES2Key");
+-        put("Cipher.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES2Key");
+-        put("Cipher.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd128BitRC2");
+-        put("Cipher.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd40BitRC2");
+-        put("Cipher.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd128BitRC4");
+-        put("Cipher.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd40BitRC4");
+-
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND3-KEYTRIPLEDES-CBC", "Cipher.PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND2-KEYTRIPLEDES-CBC", "Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC2-CBC", "Cipher.PBEWITHSHAAND128BITRC2-CBC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC2-CBC", "Cipher.PBEWITHSHAAND40BITRC2-CBC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC4", "Cipher.PBEWITHSHAAND128BITRC4");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC4", "Cipher.PBEWITHSHAAND40BITRC4");
+-
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
+-
+-        put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
++        // BEGIN android-removed
++        // put("Cipher.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES3Key");
++        // put("Cipher.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndDES3Key");
++        // put("Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES2Key");
++        // put("Cipher.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES2Key");
++        // put("Cipher.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd128BitRC2");
++        // put("Cipher.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd40BitRC2");
++        // put("Cipher.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd128BitRC4");
++        // put("Cipher.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd40BitRC4");
++        // END android-removed
++
++        // BEGIN android-changed
++        put("Alg.Alias.Cipher.PBEWITHSHA1AND3-KEYTRIPLEDES-CBC", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND2-KEYTRIPLEDES-CBC", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC2-CBC", "PBEWITHSHAAND128BITRC2-CBC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC2-CBC", "PBEWITHSHAAND40BITRC2-CBC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC4", "PBEWITHSHAAND128BITRC4");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC4", "PBEWITHSHAAND40BITRC4");
++        // END android-changed
++
++        // BEGIN android-removed
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
++        //
++        // put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
++        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
++        // END android-removed
+         
+         put("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+         put("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+         put("Cipher.PBEWITHMD5AND256BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+         
+-        put("Cipher.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndTwofish");
+-        put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
+-
+-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
+-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
++        // BEGIN android-removed
++        // put("Cipher.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndTwofish");
++        // put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
++        //
++        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
++        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
++        // END android-removed
+         put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.3", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
++        // BEGIN android-removed
++        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
++        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
++        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
++        // END android-removed
+         put("Alg.Alias.Cipher.PBEWITHSHA1ANDDESEDE", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+         //
+         // key generators.
+@@ -387,38 +455,44 @@
+         put("KeyGenerator.DES", "org.bouncycastle.jce.provider.JCEKeyGenerator$DES");
+         put("Alg.Alias.KeyGenerator." + OIWObjectIdentifiers.desCBC, "DES");
+         put("KeyGenerator.DESEDE", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
+-        put("KeyGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede3");
+-        put("KeyGenerator.DESEDEWRAP", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
+-        put("KeyGenerator.SKIPJACK", "org.bouncycastle.jce.provider.JCEKeyGenerator$Skipjack");
+-        put("KeyGenerator.BLOWFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Blowfish");
+-        put("Alg.Alias.KeyGenerator.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
+-        put("KeyGenerator.TWOFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Twofish");
+-        put("KeyGenerator.RC2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
+-        put("KeyGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
+-        put("KeyGenerator.RC4", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC4");
+-        put("Alg.Alias.KeyGenerator.ARC4", "RC4");
+-        put("Alg.Alias.KeyGenerator.1.2.840.113549.3.4", "RC4");
+-        put("KeyGenerator.RC5", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC5");
+-        put("Alg.Alias.KeyGenerator.RC5-32", "RC5");
+-        put("KeyGenerator.RC5-64", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC564");
+-        put("KeyGenerator.RC6", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC6");
+-        put("KeyGenerator.RIJNDAEL", "org.bouncycastle.jce.provider.JCEKeyGenerator$Rijndael");
+-
+-        put("KeyGenerator.SERPENT", "org.bouncycastle.jce.provider.JCEKeyGenerator$Serpent");
+-        put("KeyGenerator.SALSA20", "org.bouncycastle.jce.provider.JCEKeyGenerator$Salsa20");
+-        put("KeyGenerator.HC128", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC128");
+-        put("KeyGenerator.HC256", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC256");
+-        put("KeyGenerator.VMPC", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPC");
+-        put("KeyGenerator.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPCKSA3");
+-
+-        put("KeyGenerator.CAST6", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST6");
+-        put("KeyGenerator.TEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$TEA");
+-        put("KeyGenerator.XTEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$XTEA");
+-
+-        put("KeyGenerator.GOST28147", "org.bouncycastle.jce.provider.JCEKeyGenerator$GOST28147");
+-        put("Alg.Alias.KeyGenerator.GOST", "GOST28147");
+-        put("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147");
+-        put("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_cbc, "GOST28147");
++        // BEGIN android-removed
++        // put("KeyGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede3");
++        // END android-removed
++        // BEGIN android-removed
++        // put("KeyGenerator.DESEDEWRAP", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
++        // put("KeyGenerator.SKIPJACK", "org.bouncycastle.jce.provider.JCEKeyGenerator$Skipjack");
++        // put("KeyGenerator.BLOWFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Blowfish");
++        // put("Alg.Alias.KeyGenerator.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
++        // put("KeyGenerator.TWOFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Twofish");
++        // put("KeyGenerator.RC2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
++        // put("KeyGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
++        // put("KeyGenerator.RC4", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC4");
++        // put("Alg.Alias.KeyGenerator.ARC4", "RC4");
++        // put("Alg.Alias.KeyGenerator.1.2.840.113549.3.4", "RC4");
++        // put("KeyGenerator.RC5", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC5");
++        // put("Alg.Alias.KeyGenerator.RC5-32", "RC5");
++        // put("KeyGenerator.RC5-64", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC564");
++        // put("KeyGenerator.RC6", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC6");
++        // put("KeyGenerator.RIJNDAEL", "org.bouncycastle.jce.provider.JCEKeyGenerator$Rijndael");
++        //
++        // put("KeyGenerator.SERPENT", "org.bouncycastle.jce.provider.JCEKeyGenerator$Serpent");
++        // put("KeyGenerator.SALSA20", "org.bouncycastle.jce.provider.JCEKeyGenerator$Salsa20");
++        // put("KeyGenerator.HC128", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC128");
++        // put("KeyGenerator.HC256", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC256");
++        // put("KeyGenerator.VMPC", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPC");
++        // put("KeyGenerator.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPCKSA3");
++        // END android-removed
++
++        // BEGIN android-removed
++        // put("KeyGenerator.CAST6", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST6");
++        // put("KeyGenerator.TEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$TEA");
++        // put("KeyGenerator.XTEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$XTEA");
++        //
++        // put("KeyGenerator.GOST28147", "org.bouncycastle.jce.provider.JCEKeyGenerator$GOST28147");
++        // put("Alg.Alias.KeyGenerator.GOST", "GOST28147");
++        // put("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147");
++        // put("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_cbc, "GOST28147");
++        // END android-removed
+ 
+         //
+         // key pair generators.
+@@ -426,14 +500,18 @@
+         put("KeyPairGenerator.RSA", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$RSA");
+         put("KeyPairGenerator.DH", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$DH");
+         put("KeyPairGenerator.DSA", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$DSA");
+-        put("KeyPairGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ElGamal");
++        // BEGIN android-removed
++        // put("KeyPairGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ElGamal");
++        // END android-removed
+ 
+         put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.1", "RSA");
+         put("Alg.Alias.KeyPairGenerator.DIFFIEHELLMAN", "DH");
+         
+-        put("KeyPairGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$GOST3410");
+-        put("Alg.Alias.KeyPairGenerator.GOST-3410", "GOST3410");
+-        put("Alg.Alias.KeyPairGenerator.GOST-3410-94", "GOST3410");
++        // BEGIN android-removed
++        // put("KeyPairGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$GOST3410");
++        // put("Alg.Alias.KeyPairGenerator.GOST-3410", "GOST3410");
++        // put("Alg.Alias.KeyPairGenerator.GOST-3410-94", "GOST3410");
++        // END android-removed
+ 
+         //
+         // key factories
+@@ -441,20 +519,24 @@
+         put("KeyFactory.RSA", "org.bouncycastle.jce.provider.JDKKeyFactory$RSA");
+         put("KeyFactory.DH", "org.bouncycastle.jce.provider.JDKKeyFactory$DH");
+         put("KeyFactory.DSA", "org.bouncycastle.jce.provider.JDKKeyFactory$DSA");
+-        put("KeyFactory.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
+-        put("KeyFactory.ElGamal", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
+-
+-        put("KeyFactory.X.509", "org.bouncycastle.jce.provider.JDKKeyFactory$X509");
++        // BEGIN android-removed
++        // put("KeyFactory.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
++        // put("KeyFactory.ElGamal", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
++        //
++        // put("KeyFactory.X.509", "org.bouncycastle.jce.provider.JDKKeyFactory$X509");
++        // END android-removed
+         
+         put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA");
+         put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA");
+ 
+         put("Alg.Alias.KeyFactory.DIFFIEHELLMAN", "DH");
+ 
+-        put("KeyFactory.GOST3410", "org.bouncycastle.jce.provider.JDKKeyFactory$GOST3410");
+-        put("Alg.Alias.KeyFactory.GOST-3410", "GOST3410");
+-        put("Alg.Alias.KeyFactory.GOST-3410-94", "GOST3410");
+-        put("Alg.Alias.KeyFactory." + CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
++        // BEGIN android-removed
++        // put("KeyFactory.GOST3410", "org.bouncycastle.jce.provider.JDKKeyFactory$GOST3410");
++        // put("Alg.Alias.KeyFactory.GOST-3410", "GOST3410");
++        // put("Alg.Alias.KeyFactory.GOST-3410-94", "GOST3410");
++        // put("Alg.Alias.KeyFactory." + CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
++        // END android-removed
+ 
+         //
+         // Algorithm parameters
+@@ -462,16 +544,20 @@
+         put("AlgorithmParameters.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+         put("Alg.Alias.AlgorithmParameters." + OIWObjectIdentifiers.desCBC, "DES");
+         put("AlgorithmParameters.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
+-        put("AlgorithmParameters.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
+-        put("AlgorithmParameters.RC5", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters.RC6", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters.BLOWFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("Alg.Alias.AlgorithmParameters.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
+-        put("AlgorithmParameters.TWOFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters.SKIPJACK", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+-        put("AlgorithmParameters.RIJNDAEL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // BEGIN android-changed
++        put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "DESEDE");
++        // END android-changed
++        // BEGIN android-removed
++        // put("AlgorithmParameters.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
++        // put("AlgorithmParameters.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
++        // put("AlgorithmParameters.RC5", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // put("AlgorithmParameters.RC6", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // put("AlgorithmParameters.BLOWFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // put("Alg.Alias.AlgorithmParameters.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
++        // put("AlgorithmParameters.TWOFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // put("AlgorithmParameters.SKIPJACK", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // put("AlgorithmParameters.RIJNDAEL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
++        // END android-removed
+ 
+         
+         //
+@@ -479,80 +565,98 @@
+         //
+         put("SecretKeyFactory.DES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$DES");
+         put("SecretKeyFactory.DESEDE", "org.bouncycastle.jce.provider.JCESecretKeyFactory$DESede");
+-        put("SecretKeyFactory.PBEWITHMD2ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndDES");
+-        put("SecretKeyFactory.PBEWITHMD2ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndRC2");
++        // BEGIN android-removed
++        // put("SecretKeyFactory.PBEWITHMD2ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndDES");
++        // put("SecretKeyFactory.PBEWITHMD2ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndRC2");
++        // END android-removed
+         put("SecretKeyFactory.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndDES");
+-        put("SecretKeyFactory.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndRC2");
+-        put("SecretKeyFactory.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndDES");
+-        put("SecretKeyFactory.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndRC2");
++        // BEGIN android-removed
++        // put("SecretKeyFactory.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndRC2");
++        // put("SecretKeyFactory.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndDES");
++        // put("SecretKeyFactory.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndRC2");
++        // END android-removed
+         put("SecretKeyFactory.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES3Key");
+-        put("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES2Key");
+-        put("SecretKeyFactory.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC4");
+-        put("SecretKeyFactory.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC4");
+-        put("SecretKeyFactory.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC2");
+-        put("SecretKeyFactory.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC2");
+-        put("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndTwofish");
+-        put("SecretKeyFactory.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithRIPEMD160");
+-        put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");
+-        put("SecretKeyFactory.PBEWITHHMACTIGER", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithTiger");
++        // BEGIN android-removed
++        // put("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES2Key");
++        // put("SecretKeyFactory.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC4");
++        // put("SecretKeyFactory.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC4");
++        // put("SecretKeyFactory.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC2");
++        // put("SecretKeyFactory.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC2");
++        // put("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndTwofish");
++        // put("SecretKeyFactory.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithRIPEMD160");
++        // put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");
++        // put("SecretKeyFactory.PBEWITHHMACTIGER", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithTiger");
++        // END android-removed
+         
+         put("SecretKeyFactory.PBEWITHMD5AND128BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And128BitAESCBCOpenSSL");
+         put("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And192BitAESCBCOpenSSL");
+         put("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And256BitAESCBCOpenSSL");
+ 
+-        put("Alg.Alias.SecretKeyFactory.PBE", "PBE/PKCS5");
+-
+-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHMD5ANDDES", "PBE/PKCS5");
+-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHA1ANDDES", "PBE/PKCS5");
+-        put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+-        put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAANDTWOFISH-CBC", "PBE/PKCS12");
+-
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDDES-CBC", "PBEWITHMD2ANDDES");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDRC2-CBC", "PBEWITHMD2ANDRC2");
++        // BEGIN android-removed
++        // put("Alg.Alias.SecretKeyFactory.PBE", "PBE/PKCS5");
++        //
++        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHMD5ANDDES", "PBE/PKCS5");
++        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHA1ANDDES", "PBE/PKCS5");
++        // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
++        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
++        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBE/PKCS12");
++        // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAANDTWOFISH-CBC", "PBE/PKCS12");
++        //
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDDES-CBC", "PBEWITHMD2ANDDES");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDRC2-CBC", "PBEWITHMD2ANDRC2");
++        // END android-removed
+         put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDDES-CBC", "PBEWITHMD5ANDDES");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDRC2-CBC", "PBEWITHMD5ANDRC2");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDDES-CBC", "PBEWITHSHA1ANDDES");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDRC2-CBC", "PBEWITHSHA1ANDRC2");
+-        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
+-        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
++        // BEGIN android-removed
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDRC2-CBC", "PBEWITHMD5ANDRC2");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDDES-CBC", "PBEWITHSHA1ANDDES");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDRC2-CBC", "PBEWITHSHA1ANDRC2");
++        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
++        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
++        // END android-removed
+         put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBEWITHMD5ANDDES");
+-        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDRC2");
+-        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
+-        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
+-
+-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
+-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
++        // BEGIN android-removed
++        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDRC2");
++        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
++        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
++        // END android-removed
++
++        // BEGIN android-removed
++        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
++        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
++        // END android-removed
+         put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.3", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA", "PBEWITHHMACSHA1");
+-        put("Alg.Alias.SecretKeyFactory.1.3.14.3.2.26", "PBEWITHHMACSHA1");
++        // BEGIN android-removed
++        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
++        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
++        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA", "PBEWITHHMACSHA1");
++        // put("Alg.Alias.SecretKeyFactory.1.3.14.3.2.26", "PBEWITHHMACSHA1");
++        // END android-removed
+         put("Alg.Alias.SecretKeyFactory.PBEWithSHAAnd3KeyTripleDES", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+         
+-        put("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitAESBC");
+-        put("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd192BitAESBC");
+-        put("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd256BitAESBC");
+-        put("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And128BitAESBC");
+-        put("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And192BitAESBC");
+-        put("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And256BitAESBC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+-        put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
++        // BEGIN android-removed
++        // put("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitAESBC");
++        // put("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd192BitAESBC");
++        // put("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd256BitAESBC");
++        // put("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And128BitAESBC");
++        // put("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And192BitAESBC");
++        // put("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And256BitAESBC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
++        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
++        // END android-removed
+ 
+         addMacAlgorithms();
+ 
+@@ -561,16 +665,23 @@
+         addSignatureAlgorithms();
+ 
+     // Certification Path API
+-        put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
+-        put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
+-        put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
+-        put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
++        // BEGIN android-removed
++        // put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
++        // put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
++        // END android-removed
++        // BEGIN android-changed
++        // Use Alg.Alias so RFC3280 doesn't show up when iterating provider services, only PKIX
++        put("Alg.Alias.CertPathValidator.RFC3280", "PKIX");
++        put("Alg.Alias.CertPathBuilder.RFC3280", "PKIX");
++        // END android-changed
+         put("CertPathValidator.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
+         put("CertPathBuilder.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
+         put("CertStore.Collection", "org.bouncycastle.jce.provider.CertStoreCollectionSpi");
+-        put("CertStore.LDAP", "org.bouncycastle.jce.provider.X509LDAPCertStoreSpi");
+-        put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
+-        put("Alg.Alias.CertStore.X509LDAP", "LDAP");
++        // BEGIN android-removed
++        // put("CertStore.LDAP", "org.bouncycastle.jce.provider.X509LDAPCertStoreSpi");
++        // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
++        // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
++        // END android-removed
+     }
+ 
+     private void loadAlgorithms(String packageName, String[] names)
+@@ -631,68 +742,72 @@
+     //
+     private void addMacAlgorithms()
+     {
+-        put("Mac.DESMAC", "org.bouncycastle.jce.provider.JCEMac$DES");
+-        put("Alg.Alias.Mac.DES", "DESMAC");
+-        put("Mac.DESMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESCFB8");
+-        put("Alg.Alias.Mac.DES/CFB8", "DESMAC/CFB8");
+-
+-        put("Mac.DESEDEMAC", "org.bouncycastle.jce.provider.JCEMac$DESede");
+-        put("Alg.Alias.Mac.DESEDE", "DESEDEMAC");
+-        put("Mac.DESEDEMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESedeCFB8");
+-        put("Alg.Alias.Mac.DESEDE/CFB8", "DESEDEMAC/CFB8");
+-        
+-        put("Mac.DESWITHISO9797", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+-        put("Alg.Alias.Mac.DESISO9797MAC", "DESWITHISO9797");
+-        
+-        put("Mac.DESEDEMAC64", "org.bouncycastle.jce.provider.JCEMac$DESede64");
+-        put("Alg.Alias.Mac.DESEDE64", "DESEDEMAC64");
+-
+-        put("Mac.DESEDEMAC64WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DESede64with7816d4");
+-        put("Alg.Alias.Mac.DESEDE64WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+-        put("Alg.Alias.Mac.DESEDEISO9797ALG1MACWITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+-        put("Alg.Alias.Mac.DESEDEISO9797ALG1WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+-
+-        put("Mac.ISO9797ALG3MAC", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+-        put("Alg.Alias.Mac.ISO9797ALG3", "ISO9797ALG3MAC");
+-        put("Mac.ISO9797ALG3WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3with7816d4");
+-        put("Alg.Alias.Mac.ISO9797ALG3MACWITHISO7816-4PADDING", "ISO9797ALG3WITHISO7816-4PADDING");
+-
+-        put("Mac.SKIPJACKMAC", "org.bouncycastle.jce.provider.JCEMac$Skipjack");
+-        put("Alg.Alias.Mac.SKIPJACK", "SKIPJACKMAC");
+-        put("Mac.SKIPJACKMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$SkipjackCFB8");
+-        put("Alg.Alias.Mac.SKIPJACK/CFB8", "SKIPJACKMAC/CFB8");
+-
+-        put("Mac.RC2MAC", "org.bouncycastle.jce.provider.JCEMac$RC2");
+-        put("Alg.Alias.Mac.RC2", "RC2MAC");
+-        put("Mac.RC2MAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$RC2CFB8");
+-        put("Alg.Alias.Mac.RC2/CFB8", "RC2MAC/CFB8");
+-
+-        put("Mac.RC5MAC", "org.bouncycastle.jce.provider.JCEMac$RC5");
+-        put("Alg.Alias.Mac.RC5", "RC5MAC");
+-        put("Mac.RC5MAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$RC5CFB8");
+-        put("Alg.Alias.Mac.RC5/CFB8", "RC5MAC/CFB8");
+-
+-        put("Mac.GOST28147MAC", "org.bouncycastle.jce.provider.JCEMac$GOST28147");
+-        put("Alg.Alias.Mac.GOST28147", "GOST28147MAC");
+-
+-        put("Mac.VMPCMAC", "org.bouncycastle.jce.provider.JCEMac$VMPC");
+-        put("Alg.Alias.Mac.VMPC", "VMPCMAC");
+-        put("Alg.Alias.Mac.VMPC-MAC", "VMPCMAC");
+-
+-        put("Mac.OLDHMACSHA384", "org.bouncycastle.jce.provider.JCEMac$OldSHA384");
+-
+-        put("Mac.OLDHMACSHA512", "org.bouncycastle.jce.provider.JCEMac$OldSHA512");
+-
+-        addHMACAlgorithm("MD2", "org.bouncycastle.jce.provider.JCEMac$MD2", "org.bouncycastle.jce.provider.JCEKeyGenerator$MD2HMAC");
+-        addHMACAlgorithm("MD4", "org.bouncycastle.jce.provider.JCEMac$MD4", "org.bouncycastle.jce.provider.JCEKeyGenerator$MD4HMAC");
++        // BEGIN android-removed
++        // put("Mac.DESMAC", "org.bouncycastle.jce.provider.JCEMac$DES");
++        // put("Alg.Alias.Mac.DES", "DESMAC");
++        // put("Mac.DESMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESCFB8");
++        // put("Alg.Alias.Mac.DES/CFB8", "DESMAC/CFB8");
++        //
++        // put("Mac.DESEDEMAC", "org.bouncycastle.jce.provider.JCEMac$DESede");
++        // put("Alg.Alias.Mac.DESEDE", "DESEDEMAC");
++        // put("Mac.DESEDEMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESedeCFB8");
++        // put("Alg.Alias.Mac.DESEDE/CFB8", "DESEDEMAC/CFB8");
++        //
++        // put("Mac.DESWITHISO9797", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
++        // put("Alg.Alias.Mac.DESISO9797MAC", "DESWITHISO9797");
++        //
++        // put("Mac.DESEDEMAC64", "org.bouncycastle.jce.provider.JCEMac$DESede64");
++        // put("Alg.Alias.Mac.DESEDE64", "DESEDEMAC64");
++        //
++        // put("Mac.DESEDEMAC64WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DESede64with7816d4");
++        // put("Alg.Alias.Mac.DESEDE64WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
++        // put("Alg.Alias.Mac.DESEDEISO9797ALG1MACWITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
++        // put("Alg.Alias.Mac.DESEDEISO9797ALG1WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
++        //
++        // put("Mac.ISO9797ALG3MAC", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
++        // put("Alg.Alias.Mac.ISO9797ALG3", "ISO9797ALG3MAC");
++        // put("Mac.ISO9797ALG3WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3with7816d4");
++        // put("Alg.Alias.Mac.ISO9797ALG3MACWITHISO7816-4PADDING", "ISO9797ALG3WITHISO7816-4PADDING");
++        //
++        // put("Mac.SKIPJACKMAC", "org.bouncycastle.jce.provider.JCEMac$Skipjack");
++        // put("Alg.Alias.Mac.SKIPJACK", "SKIPJACKMAC");
++        // put("Mac.SKIPJACKMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$SkipjackCFB8");
++        // put("Alg.Alias.Mac.SKIPJACK/CFB8", "SKIPJACKMAC/CFB8");
++        //
++        // put("Mac.RC2MAC", "org.bouncycastle.jce.provider.JCEMac$RC2");
++        // put("Alg.Alias.Mac.RC2", "RC2MAC");
++        // put("Mac.RC2MAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$RC2CFB8");
++        // put("Alg.Alias.Mac.RC2/CFB8", "RC2MAC/CFB8");
++        //
++        // put("Mac.RC5MAC", "org.bouncycastle.jce.provider.JCEMac$RC5");
++        // put("Alg.Alias.Mac.RC5", "RC5MAC");
++        // put("Mac.RC5MAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$RC5CFB8");
++        // put("Alg.Alias.Mac.RC5/CFB8", "RC5MAC/CFB8");
++        //
++        // put("Mac.GOST28147MAC", "org.bouncycastle.jce.provider.JCEMac$GOST28147");
++        // put("Alg.Alias.Mac.GOST28147", "GOST28147MAC");
++        //
++        // put("Mac.VMPCMAC", "org.bouncycastle.jce.provider.JCEMac$VMPC");
++        // put("Alg.Alias.Mac.VMPC", "VMPCMAC");
++        // put("Alg.Alias.Mac.VMPC-MAC", "VMPCMAC");
++        //
++        // put("Mac.OLDHMACSHA384", "org.bouncycastle.jce.provider.JCEMac$OldSHA384");
++        //
++        // put("Mac.OLDHMACSHA512", "org.bouncycastle.jce.provider.JCEMac$OldSHA512");
++        //
++        // addHMACAlgorithm("MD2", "org.bouncycastle.jce.provider.JCEMac$MD2", "org.bouncycastle.jce.provider.JCEKeyGenerator$MD2HMAC");
++        // addHMACAlgorithm("MD4", "org.bouncycastle.jce.provider.JCEMac$MD4", "org.bouncycastle.jce.provider.JCEKeyGenerator$MD4HMAC");
++        // END android-removed
+         addHMACAlgorithm("MD5", "org.bouncycastle.jce.provider.JCEMac$MD5", "org.bouncycastle.jce.provider.JCEKeyGenerator$MD5HMAC");
+         addHMACAlias("MD5", IANAObjectIdentifiers.hmacMD5);
+ 
+         addHMACAlgorithm("SHA1", "org.bouncycastle.jce.provider.JCEMac$SHA1", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA1");
+         addHMACAlias("SHA1", PKCSObjectIdentifiers.id_hmacWithSHA1);
+         addHMACAlias("SHA1", IANAObjectIdentifiers.hmacSHA1);
+-        addHMACAlgorithm("SHA224", "org.bouncycastle.jce.provider.JCEMac$SHA224", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA224");
+-        addHMACAlias("SHA224", PKCSObjectIdentifiers.id_hmacWithSHA224);
++        // BEGIN android-removed
++        // addHMACAlgorithm("SHA224", "org.bouncycastle.jce.provider.JCEMac$SHA224", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA224");
++        // addHMACAlias("SHA224", PKCSObjectIdentifiers.id_hmacWithSHA224);
++        // END android-removed
+         addHMACAlgorithm("SHA256", "org.bouncycastle.jce.provider.JCEMac$SHA256", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA256");
+         addHMACAlias("SHA256", PKCSObjectIdentifiers.id_hmacWithSHA256);
+         addHMACAlgorithm("SHA384", "org.bouncycastle.jce.provider.JCEMac$SHA384", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA384");
+@@ -700,17 +815,19 @@
+         addHMACAlgorithm("SHA512", "org.bouncycastle.jce.provider.JCEMac$SHA512", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA512");
+         addHMACAlias("SHA512", PKCSObjectIdentifiers.id_hmacWithSHA512);
+ 
+-        addHMACAlgorithm("RIPEMD128", "org.bouncycastle.jce.provider.JCEMac$RIPEMD128", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD128HMAC");
+-        addHMACAlgorithm("RIPEMD160", "org.bouncycastle.jce.provider.JCEMac$RIPEMD160", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD160HMAC");
+-        addHMACAlias("RIPEMD160", IANAObjectIdentifiers.hmacRIPEMD160);
+-
+-        addHMACAlgorithm("TIGER", "org.bouncycastle.jce.provider.JCEMac$Tiger", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACTIGER");
+-        addHMACAlias("TIGER", IANAObjectIdentifiers.hmacTIGER);
+-
+-        put("Mac.PBEWITHHMACSHA", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
+-        put("Mac.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
+-        put("Mac.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCEMac$PBEWithRIPEMD160");
+-        put("Alg.Alias.Mac.1.3.14.3.2.26", "PBEWITHHMACSHA");
++        // BEGIN android-removed
++        // addHMACAlgorithm("RIPEMD128", "org.bouncycastle.jce.provider.JCEMac$RIPEMD128", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD128HMAC");
++        // addHMACAlgorithm("RIPEMD160", "org.bouncycastle.jce.provider.JCEMac$RIPEMD160", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD160HMAC");
++        // addHMACAlias("RIPEMD160", IANAObjectIdentifiers.hmacRIPEMD160);
++        //
++        // addHMACAlgorithm("TIGER", "org.bouncycastle.jce.provider.JCEMac$Tiger", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACTIGER");
++        // addHMACAlias("TIGER", IANAObjectIdentifiers.hmacTIGER);
++        //
++        // put("Mac.PBEWITHHMACSHA", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
++        // put("Mac.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
++        // put("Mac.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCEMac$PBEWithRIPEMD160");
++        // put("Alg.Alias.Mac.1.3.14.3.2.26", "PBEWITHHMACSHA");
++        // END android-removed
+     }
+ 
+     private void addHMACAlgorithm(
+@@ -747,9 +864,11 @@
+         put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
+         put("Alg.Alias.MessageDigest.SHA", "SHA-1");
+         put("Alg.Alias.MessageDigest." + OIWObjectIdentifiers.idSHA1, "SHA-1");
+-        put("MessageDigest.SHA-224", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA224");
+-        put("Alg.Alias.MessageDigest.SHA224", "SHA-224");
+-        put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha224, "SHA-224");
++        // BEGIN android-removed
++        // put("MessageDigest.SHA-224", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA224");
++        // put("Alg.Alias.MessageDigest.SHA224", "SHA-224");
++        // put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha224, "SHA-224");
++        // END android-removed
+         put("MessageDigest.SHA-256", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA256");
+         put("Alg.Alias.MessageDigest.SHA256", "SHA-256");
+         put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha256, "SHA-256");
+@@ -760,27 +879,31 @@
+         put("Alg.Alias.MessageDigest.SHA512", "SHA-512");
+         put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512, "SHA-512");
+         
+-        put("MessageDigest.MD2", "org.bouncycastle.jce.provider.JDKMessageDigest$MD2");
+-        put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md2, "MD2");
+-        put("MessageDigest.MD4", "org.bouncycastle.jce.provider.JDKMessageDigest$MD4");
+-        put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md4, "MD4");
++        // BEGIN android-removed
++        // put("MessageDigest.MD2", "org.bouncycastle.jce.provider.JDKMessageDigest$MD2");
++        // put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md2, "MD2");
++        // put("MessageDigest.MD4", "org.bouncycastle.jce.provider.JDKMessageDigest$MD4");
++        // put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md4, "MD4");
++        // END android-removed
+         put("MessageDigest.MD5", "org.bouncycastle.jce.provider.JDKMessageDigest$MD5");
+         put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md5, "MD5");
+-        put("MessageDigest.RIPEMD128", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD128");
+-        put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
+-        put("MessageDigest.RIPEMD160", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD160");
+-        put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
+-        put("MessageDigest.RIPEMD256", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD256");
+-        put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
+-        put("MessageDigest.RIPEMD320", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD320");
+-        put("MessageDigest.Tiger", "org.bouncycastle.jce.provider.JDKMessageDigest$Tiger");
+-        
+-        put("MessageDigest.WHIRLPOOL", "org.bouncycastle.jce.provider.JDKMessageDigest$Whirlpool");
+-        
+-        put("MessageDigest.GOST3411", "org.bouncycastle.jce.provider.JDKMessageDigest$GOST3411");
+-        put("Alg.Alias.MessageDigest.GOST", "GOST3411");
+-        put("Alg.Alias.MessageDigest.GOST-3411", "GOST3411");
+-        put("Alg.Alias.MessageDigest." + CryptoProObjectIdentifiers.gostR3411, "GOST3411");
++        // BEGIN android-removed
++        // put("MessageDigest.RIPEMD128", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD128");
++        // put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
++        // put("MessageDigest.RIPEMD160", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD160");
++        // put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
++        // put("MessageDigest.RIPEMD256", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD256");
++        // put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
++        // put("MessageDigest.RIPEMD320", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD320");
++        // put("MessageDigest.Tiger", "org.bouncycastle.jce.provider.JDKMessageDigest$Tiger");
++        
++        // put("MessageDigest.WHIRLPOOL", "org.bouncycastle.jce.provider.JDKMessageDigest$Whirlpool");
++        
++        // put("MessageDigest.GOST3411", "org.bouncycastle.jce.provider.JDKMessageDigest$GOST3411");
++        // put("Alg.Alias.MessageDigest.GOST", "GOST3411");
++        // put("Alg.Alias.MessageDigest.GOST-3411", "GOST3411");
++        // put("Alg.Alias.MessageDigest." + CryptoProObjectIdentifiers.gostR3411, "GOST3411");
++        // END android-removed
+     }
+     
+     //
+@@ -788,55 +911,70 @@
+     //
+     private void addSignatureAlgorithms()
+     {
+-        put("Signature.MD2WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD2WithRSAEncryption");
+-        put("Signature.MD4WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD4WithRSAEncryption");
++        // BEGIN android-removed
++        // Dropping MD2
++        // put("Signature.MD2WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD2WithRSAEncryption");
++        // put("Signature.MD4WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD4WithRSAEncryption");
++        // END android-removed
+         put("Signature.MD5WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD5WithRSAEncryption");
+         put("Signature.SHA1WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA1WithRSAEncryption");
+-        put("Signature.SHA224WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA224WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Signature.SHA224WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA224WithRSAEncryption");
++        // END android-removed
+         put("Signature.SHA256WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA256WithRSAEncryption");
+         put("Signature.SHA384WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA384WithRSAEncryption");
+         put("Signature.SHA512WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA512WithRSAEncryption");
+-        put("Signature.RIPEMD160WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD160WithRSAEncryption");
+-        put("Signature.RIPEMD128WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD128WithRSAEncryption");
+-        put("Signature.RIPEMD256WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD256WithRSAEncryption");
+-        put("Signature.DSA", "org.bouncycastle.jce.provider.JDKDSASigner$stdDSA");
++        // BEGIN android-removed
++        // put("Signature.RIPEMD160WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD160WithRSAEncryption");
++        // put("Signature.RIPEMD128WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD128WithRSAEncryption");
++        // put("Signature.RIPEMD256WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD256WithRSAEncryption");
++        // END android-removed
++        // BEGIN android-changed
++        put("Signature.SHA1withDSA", "org.bouncycastle.jce.provider.JDKDSASigner$stdDSA");
++        // END android-changed
+         put("Signature.NONEWITHDSA", "org.bouncycastle.jce.provider.JDKDSASigner$noneDSA");
+-        put("Signature.SHA1withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$SHA1WithRSAEncryption");
+-        put("Signature.MD5withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$MD5WithRSAEncryption");
+-        put("Signature.RIPEMD160withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$RIPEMD160WithRSAEncryption");
+-
+-        put("Signature.RSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
+-        put("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
+-        put("Signature.SHA1withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA1withRSA");
+-        put("Signature.SHA224withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA224withRSA");
+-        put("Signature.SHA256withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA256withRSA");
+-        put("Signature.SHA384withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA384withRSA");
+-        put("Signature.SHA512withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA512withRSA");
+-
+-        put("Signature.RSA", "org.bouncycastle.jce.provider.JDKDigestSignature$noneRSA");
+-        put("Signature.RAWRSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$nonePSS");
++        // BEGIN android-removed
++        // put("Signature.SHA1withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$SHA1WithRSAEncryption");
++        // put("Signature.MD5withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$MD5WithRSAEncryption");
++        // put("Signature.RIPEMD160withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$RIPEMD160WithRSAEncryption");
++        //
++        // put("Signature.RSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
++        // put("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
++        // put("Signature.SHA1withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA1withRSA");
++        // put("Signature.SHA224withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA224withRSA");
++        // put("Signature.SHA256withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA256withRSA");
++        // put("Signature.SHA384withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA384withRSA");
++        // put("Signature.SHA512withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA512withRSA");
++        //
++        // put("Signature.RSA", "org.bouncycastle.jce.provider.JDKDigestSignature$noneRSA");
++        // put("Signature.RAWRSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$nonePSS");
++        // END android-removed
+ 
+         put("Alg.Alias.Signature.RAWDSA", "NONEWITHDSA");
+ 
+-        put("Alg.Alias.Signature.RAWRSA", "RSA");
+-        put("Alg.Alias.Signature.NONEWITHRSA", "RSA");
+-        put("Alg.Alias.Signature.RAWRSAPSS", "RAWRSASSA-PSS");
+-        put("Alg.Alias.Signature.NONEWITHRSAPSS", "RAWRSASSA-PSS");
+-        put("Alg.Alias.Signature.NONEWITHRSASSA-PSS", "RAWRSASSA-PSS");
+-
+-        put("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
+-
+-        put("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS");
+-        put("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS");
+-        put("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS");
+-        put("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS");
+-        put("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS");
+-        
+-        put("Alg.Alias.Signature.MD2withRSAEncryption", "MD2WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD4withRSAEncryption", "MD4WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.RAWRSA", "RSA");
++        // put("Alg.Alias.Signature.NONEWITHRSA", "RSA");
++        // put("Alg.Alias.Signature.RAWRSAPSS", "RAWRSASSA-PSS");
++        // put("Alg.Alias.Signature.NONEWITHRSAPSS", "RAWRSASSA-PSS");
++        // put("Alg.Alias.Signature.NONEWITHRSASSA-PSS", "RAWRSASSA-PSS");
++        //
++        // put("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
++        //
++        // put("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS");
++        // put("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS");
++        // put("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS");
++        // put("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS");
++        // put("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS");
++        //
++        // put("Alg.Alias.Signature.MD2withRSAEncryption", "MD2WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD4withRSAEncryption", "MD4WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature.MD5withRSAEncryption", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA1withRSAEncryption", "SHA1WithRSAEncryption");
+-        put("Alg.Alias.Signature.SHA224withRSAEncryption", "SHA224WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.SHA224withRSAEncryption", "SHA224WithRSAEncryption");
++        // END android-removed
+ 
+         put("Alg.Alias.Signature.SHA256withRSAEncryption", "SHA256WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA384withRSAEncryption", "SHA384WithRSAEncryption");
+@@ -850,24 +988,30 @@
+         put("Alg.Alias.Signature.SHA384WITHRSAENCRYPTION", "SHA384WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA512WITHRSAENCRYPTION", "SHA512WithRSAEncryption");
+ 
+-        put("Alg.Alias.Signature.RIPEMD160withRSAEncryption", "RIPEMD160WithRSAEncryption");
+-
+-        put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD2WithRSA", "MD2WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD2withRSA", "MD2WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD2/RSA", "MD2WithRSAEncryption");
++        // BEGIN android-removed
++        // Dropping MD2
++        // put("Alg.Alias.Signature.RIPEMD160withRSAEncryption", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD2WithRSA", "MD2WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD2withRSA", "MD2WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD2/RSA", "MD2WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature.MD5WithRSA", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature.MD5withRSA", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD4WithRSA", "MD4WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD4withRSA", "MD4WithRSAEncryption");
+-        put("Alg.Alias.Signature.MD4/RSA", "MD4WithRSAEncryption");
+-        put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.MD4WithRSA", "MD4WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD4withRSA", "MD4WithRSAEncryption");
++        // put("Alg.Alias.Signature.MD4/RSA", "MD4WithRSAEncryption");
++        // put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA1withRSA", "SHA1WithRSAEncryption");
+-        put("Alg.Alias.Signature.SHA224WithRSA", "SHA224WithRSAEncryption");
+-        put("Alg.Alias.Signature.SHA224withRSA", "SHA224WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.SHA224WithRSA", "SHA224WithRSAEncryption");
++        // put("Alg.Alias.Signature.SHA224withRSA", "SHA224WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA256withRSA", "SHA256WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption");
+@@ -877,92 +1021,110 @@
+         put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption");
+         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1WithRSAEncryption");
+-        put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WithRSAEncryption");
+         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WithRSAEncryption");
+         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WithRSAEncryption");
+         put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSAEncryption");
+         put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSAEncryption");
+         put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD160WithRSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD160withRSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD128WithRSA", "RIPEMD128WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD128withRSA", "RIPEMD128WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD256WithRSA", "RIPEMD256WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD256withRSA", "RIPEMD256WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD-160/RSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RMD160withRSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RMD160/RSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.1.3.36.3.3.1.2", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.1.3.36.3.3.1.3", "RIPEMD128WithRSAEncryption");
+-        put("Alg.Alias.Signature.1.3.36.3.3.1.4", "RIPEMD256WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.RIPEMD160WithRSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD160withRSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD128WithRSA", "RIPEMD128WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD128withRSA", "RIPEMD128WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD256WithRSA", "RIPEMD256WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD256withRSA", "RIPEMD256WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD-160/RSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RMD160withRSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RMD160/RSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.1.3.36.3.3.1.2", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.1.3.36.3.3.1.3", "RIPEMD128WithRSAEncryption");
++        // put("Alg.Alias.Signature.1.3.36.3.3.1.4", "RIPEMD256WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature." + OIWObjectIdentifiers.sha1WithRSA, "SHA1WithRSAEncryption");
+         
+-        put("Alg.Alias.Signature.MD2WITHRSAENCRYPTION", "MD2WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.MD2WITHRSAENCRYPTION", "MD2WithRSAEncryption");
++        // END android-removed
+         put("Alg.Alias.Signature.MD5WITHRSAENCRYPTION", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA1WITHRSAENCRYPTION", "SHA1WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD160WITHRSAENCRYPTION", "RIPEMD160WithRSAEncryption");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.RIPEMD160WITHRSAENCRYPTION", "RIPEMD160WithRSAEncryption");
++        // END android-removed
+ 
+         put("Alg.Alias.Signature.MD5WITHRSA", "MD5WithRSAEncryption");
+         put("Alg.Alias.Signature.SHA1WITHRSA", "SHA1WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RMD160WITHRSA", "RIPEMD160WithRSAEncryption");
+-        put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
+-
+-        addSignatureAlgorithm("SHA224", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224);
+-        addSignatureAlgorithm("SHA256", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256);
+-        addSignatureAlgorithm("SHA384", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384);
+-        addSignatureAlgorithm("SHA512", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa512", NISTObjectIdentifiers.dsa_with_sha512);
+-
+-        put("Alg.Alias.Signature.SHA/DSA", "DSA");
+-        put("Alg.Alias.Signature.SHA1withDSA", "DSA");
+-        put("Alg.Alias.Signature.SHA1WITHDSA", "DSA");
+-        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "DSA");
+-        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "DSA");
+-        put("Alg.Alias.Signature.DSAwithSHA1", "DSA");
+-        put("Alg.Alias.Signature.DSAWITHSHA1", "DSA");
+-        put("Alg.Alias.Signature.SHA1WithDSA", "DSA");
+-        put("Alg.Alias.Signature.DSAWithSHA1", "DSA");
+-        put("Alg.Alias.Signature.1.2.840.10040.4.3", "DSA");
+-        put("Alg.Alias.Signature.MD5WithRSA/ISO9796-2", "MD5withRSA/ISO9796-2");
+-        put("Alg.Alias.Signature.SHA1WithRSA/ISO9796-2", "SHA1withRSA/ISO9796-2");
+-        put("Alg.Alias.Signature.RIPEMD160WithRSA/ISO9796-2", "RIPEMD160withRSA/ISO9796-2");
+-        
+-        put("Signature.ECGOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$ecgost3410");
+-        put("Alg.Alias.Signature.ECGOST-3410", "ECGOST3410");
+-        put("Alg.Alias.Signature.GOST-3410-2001", "ECGOST3410");
+-        put("Alg.Alias.Signature.GOST3411withECGOST3410", "ECGOST3410");
+-        put("Alg.Alias.Signature.GOST3411WITHECGOST3410", "ECGOST3410");
+-        put("Alg.Alias.Signature.GOST3411WithECGOST3410", "ECGOST3410");
+-        put("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
+-        
+-        put("Signature.GOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$gost3410");
+-        put("Alg.Alias.Signature.GOST-3410", "GOST3410");
+-        put("Alg.Alias.Signature.GOST-3410-94", "GOST3410");
+-        put("Alg.Alias.Signature.GOST3411withGOST3410", "GOST3410");
+-        put("Alg.Alias.Signature.GOST3411WITHGOST3410", "GOST3410");
+-        put("Alg.Alias.Signature.GOST3411WithGOST3410", "GOST3410");
+-        put("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RMD160WITHRSA", "RIPEMD160WithRSAEncryption");
++        // put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
++        // END android-removed
++
++        // BEGIN android-removed
++        // addSignatureAlgorithm("SHA224", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224);
++        // addSignatureAlgorithm("SHA256", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256);
++        // addSignatureAlgorithm("SHA384", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384);
++        // addSignatureAlgorithm("SHA512", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa512", NISTObjectIdentifiers.dsa_with_sha512);
++        // END android-removed
++
++        // BEGIN android-changed
++        put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA");
++        put("Alg.Alias.Signature.DSA", "SHA1withDSA");
++        put("Alg.Alias.Signature.SHA1WITHDSA", "SHA1withDSA");
++        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "SHA1withDSA");
++        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA");
++        put("Alg.Alias.Signature.DSAwithSHA1", "SHA1withDSA");
++        put("Alg.Alias.Signature.DSAWITHSHA1", "SHA1withDSA");
++        put("Alg.Alias.Signature.SHA1WithDSA", "SHA1withDSA");
++        put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
++        put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA");
++        // END android-changed
++        // BEGIN android-removed
++        // put("Alg.Alias.Signature.MD5WithRSA/ISO9796-2", "MD5withRSA/ISO9796-2");
++        // put("Alg.Alias.Signature.SHA1WithRSA/ISO9796-2", "SHA1withRSA/ISO9796-2");
++        // put("Alg.Alias.Signature.RIPEMD160WithRSA/ISO9796-2", "RIPEMD160withRSA/ISO9796-2");
++        //
++        // put("Signature.ECGOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$ecgost3410");
++        // put("Alg.Alias.Signature.ECGOST-3410", "ECGOST3410");
++        // put("Alg.Alias.Signature.GOST-3410-2001", "ECGOST3410");
++        // put("Alg.Alias.Signature.GOST3411withECGOST3410", "ECGOST3410");
++        // put("Alg.Alias.Signature.GOST3411WITHECGOST3410", "ECGOST3410");
++        // put("Alg.Alias.Signature.GOST3411WithECGOST3410", "ECGOST3410");
++        // put("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
++        //
++        // put("Signature.GOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$gost3410");
++        // put("Alg.Alias.Signature.GOST-3410", "GOST3410");
++        // put("Alg.Alias.Signature.GOST-3410-94", "GOST3410");
++        // put("Alg.Alias.Signature.GOST3411withGOST3410", "GOST3410");
++        // put("Alg.Alias.Signature.GOST3411WITHGOST3410", "GOST3410");
++        // put("Alg.Alias.Signature.GOST3411WithGOST3410", "GOST3410");
++        // put("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
++        // END android-removed
+     }
+ 
+-    private void addSignatureAlgorithm(
+-        String digest,
+-        String algorithm,
+-        String className,
+-        DERObjectIdentifier oid)
+-    {
+-        String mainName = digest + "WITH" + algorithm;
+-        String jdk11Variation1 = digest + "with" + algorithm;
+-        String jdk11Variation2 = digest + "With" + algorithm;
+-        String alias = digest + "/" + algorithm;
+-
+-        put("Signature." + mainName, className);
+-        put("Alg.Alias.Signature." + jdk11Variation1, mainName);
+-        put("Alg.Alias.Signature." + jdk11Variation2, mainName);
+-        put("Alg.Alias.Signature." + alias, mainName);
+-        put("Alg.Alias.Signature." + oid, mainName);
+-        put("Alg.Alias.Signature.OID." + oid, mainName);
+-    }
++    // BEGIN android-removed
++    // private void addSignatureAlgorithm(
++    //     String digest,
++    //     String algorithm,
++    //     String className,
++    //     DERObjectIdentifier oid)
++    // {
++    //     String mainName = digest + "WITH" + algorithm;
++    //     String jdk11Variation1 = digest + "with" + algorithm;
++    //     String jdk11Variation2 = digest + "With" + algorithm;
++    //     String alias = digest + "/" + algorithm;
++    //
++    //     put("Signature." + mainName, className);
++    //     put("Alg.Alias.Signature." + jdk11Variation1, mainName);
++    //     put("Alg.Alias.Signature." + jdk11Variation2, mainName);
++    //     put("Alg.Alias.Signature." + alias, mainName);
++    //     put("Alg.Alias.Signature." + oid, mainName);
++    //     put("Alg.Alias.Signature.OID." + oid, mainName);
++    // }
++    // END android-removed
+ 
+     public void setParameter(String parameterName, Object parameter)
+     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java bcprov-jdk16-145/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java	2010-06-25 23:17:26.000000000 +0000
+@@ -24,6 +24,7 @@
+ import java.security.spec.DSAPublicKeySpec;
+ import java.text.ParseException;
+ import java.util.ArrayList;
++import java.util.Arrays;
+ import java.util.Collection;
+ import java.util.Date;
+ import java.util.Enumeration;
+@@ -35,6 +36,10 @@
+ 
+ import javax.security.auth.x500.X500Principal;
+ 
++// BEGIN android-added
++import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters;
++
++// END android-added
+ import org.bouncycastle.asn1.ASN1InputStream;
+ import org.bouncycastle.asn1.ASN1Object;
+ import org.bouncycastle.asn1.ASN1OctetString;
+@@ -59,13 +64,17 @@
+ import org.bouncycastle.asn1.x509.PolicyInformation;
+ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+ import org.bouncycastle.asn1.x509.X509Extensions;
+-import org.bouncycastle.jce.X509LDAPCertStoreParameters;
++// BEGIN android-removed
++// import org.bouncycastle.jce.X509LDAPCertStoreParameters;
++// END android-removed
+ import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+ import org.bouncycastle.util.Selector;
+ import org.bouncycastle.util.StoreException;
+ import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+ import org.bouncycastle.x509.ExtendedPKIXParameters;
+-import org.bouncycastle.x509.X509AttributeCertStoreSelector;
++// BEGIN android-removed
++// import org.bouncycastle.x509.X509AttributeCertStoreSelector;
++// END android-removed
+ import org.bouncycastle.x509.X509AttributeCertificate;
+ import org.bouncycastle.x509.X509CRLStoreSelector;
+ import org.bouncycastle.x509.X509CertStoreSelector;
+@@ -110,29 +119,32 @@
+         "privilegeWithdrawn",
+         "aACompromise" };
+     
+-    /**
+-     * Search the given Set of TrustAnchor's for one that is the
+-     * issuer of the given X509 certificate. Uses the default provider
+-     * for signature verification.
+-     *
+-     * @param cert the X509 certificate
+-     * @param trustAnchors a Set of TrustAnchor's
+-     *
+-     * @return the <code>TrustAnchor</code> object if found or
+-     * <code>null</code> if not.
+-     *
+-     * @exception AnnotatedException
+-     *                if a TrustAnchor was found but the signature verification
+-     *                on the given certificate has thrown an exception.
+-     */
+-    protected static TrustAnchor findTrustAnchor(
+-        X509Certificate cert,
+-        Set             trustAnchors)
+-            throws AnnotatedException
+-    {
+-        return findTrustAnchor(cert, trustAnchors, null);
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * Search the given Set of TrustAnchor's for one that is the
++    //  * issuer of the given X509 certificate. Uses the default provider
++    //  * for signature verification.
++    //  *
++    //  * @param cert the X509 certificate
++    //  * @param trustAnchors a Set of TrustAnchor's
++    //  *
++    //  * @return the <code>TrustAnchor</code> object if found or
++    //  * <code>null</code> if not.
++    //  *
++    //  * @exception AnnotatedException
++    //  *                if a TrustAnchor was found but the signature verification
++    //  *                on the given certificate has thrown an exception.
++    //  */
++    // protected static TrustAnchor findTrustAnchor(
++    //     X509Certificate cert,
++    //     Set             trustAnchors)
++    //         throws AnnotatedException
++    // {
++    //     return findTrustAnchor(cert, trustAnchors, null);
++    // }
++    // END android-removed
+     
++    // BEGIN android-changed
+     /**
+      * Search the given Set of TrustAnchor's for one that is the
+      * issuer of the given X509 certificate. Uses the specified
+@@ -140,8 +152,7 @@
+      * if null.
+      *
+      * @param cert the X509 certificate
+-     * @param trustAnchors a Set of TrustAnchor's
+-     * @param sigProvider the provider to use for signature verification
++     * @param params used to find the trust anchors and signature provider
+      *
+      * @return the <code>TrustAnchor</code> object if found or
+      * <code>null</code> if not.
+@@ -152,10 +163,21 @@
+      */
+     protected static TrustAnchor findTrustAnchor(
+         X509Certificate cert,
+-        Set             trustAnchors,
+-        String          sigProvider) 
++        PKIXParameters  params)
+             throws AnnotatedException
++    // END android-changed
+     {
++        // BEGIN android-changed
++        // If we have a trust anchor index, use it.
++        if (params instanceof IndexedPKIXParameters) {
++            try {
++                IndexedPKIXParameters indexed = (IndexedPKIXParameters) params;
++                return indexed.findTrustAnchor(cert);
++            } catch (CertPathValidatorException e) {
++                throw new AnnotatedException(e.getMessage(), e);
++            }
++        }
++        // END android-changed
+         TrustAnchor trust = null;
+         PublicKey trustPublicKey = null;
+         Exception invalidKeyEx = null;
+@@ -172,21 +194,49 @@
+             throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex);
+         }
+ 
+-        Iterator iter = trustAnchors.iterator();
++        // BEGIN android-changed
++        Iterator iter = params.getTrustAnchors().iterator();
++        // END android-changed
++        // BEGIN android-added
++        byte[] certBytes = null;
++        try {
++            certBytes = cert.getEncoded();
++        } catch (Exception e) {
++            // ignore, just continue
++        }
++        // END android-added
+         while (iter.hasNext() && trust == null)
+         {
+             trust = (TrustAnchor) iter.next();
+-            if (trust.getTrustedCert() != null)
++            // BEGIN android-changed
++            X509Certificate trustCert = trust.getTrustedCert();
++            // END android-changed
++            // BEGIN android-added
++            // If the trust anchor is identical to the certificate we're
++            // done. Just return the anchor.
++            // There is similar code in PKIXCertPathValidatorSpi.
++            try {
++                byte[] trustBytes = trustCert.getEncoded();
++                if (certBytes != null && Arrays.equals(trustBytes, certBytes)) {
++                    return trust;
++                }
++            } catch (Exception e) {
++                // ignore, continue and verify the certificate
++            }
++            // END android-added
++            // BEGIN android-changed
++            if (trustCert != null)
+             {
+-                if (certSelectX509.match(trust.getTrustedCert()))
++                if (certSelectX509.match(trustCert))
+                 {
+-                    trustPublicKey = trust.getTrustedCert().getPublicKey();
++                    trustPublicKey = trustCert.getPublicKey();
+                 }
+                 else
+                 {
+                     trust = null;
+                 }
+             }
++            // END android-changed
+             else if (trust.getCAName() != null
+                     && trust.getCAPublicKey() != null)
+             {
+@@ -216,7 +266,9 @@
+             {
+                 try
+                 {
+-                    verifyX509Certificate(cert, trustPublicKey, sigProvider);
++                    // BEGIN android-changed
++                    verifyX509Certificate(cert, trustPublicKey, params.getSigProvider());
++                    // END android-changed
+                 }
+                 catch (Exception ex)
+                 {
+@@ -248,7 +300,9 @@
+             {
+                 // look for URI
+                 List list = (List) it.next();
+-                if (list.get(0).equals(new Integer(GeneralName.uniformResourceIdentifier)))
++                // BEGIN android-changed
++                if (list.get(0).equals(Integer.valueOf(GeneralName.uniformResourceIdentifier)))
++                // END android-changed
+                 {
+                     // found
+                     String temp = (String) list.get(1);
+@@ -721,38 +775,40 @@
+         {
+             try
+             {
+-                if (location.startsWith("ldap://"))
+-                {
+-                    // ldap://directory.d-trust.net/CN=D-TRUST
+-                    // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
+-                    // skip "ldap://"
+-                    location = location.substring(7);
+-                    // after first / baseDN starts
+-                    String base = null;
+-                    String url = null;
+-                    if (location.indexOf("/") != -1)
+-                    {
+-                        base = location.substring(location.indexOf("/"));
+-                        // URL
+-                        url = "ldap://"
+-                            + location.substring(0, location.indexOf("/"));
+-                    }
+-                    else
+-                    {
+-                        url = "ldap://" + location;
+-                    }
+-                    // use all purpose parameters
+-                    X509LDAPCertStoreParameters params = new X509LDAPCertStoreParameters.Builder(
+-                        url, base).build();
+-                    pkixParams.addAdditionalStore(X509Store.getInstance(
+-                        "CERTIFICATE/LDAP", params, "BC"));
+-                    pkixParams.addAdditionalStore(X509Store.getInstance(
+-                        "CRL/LDAP", params, "BC"));
+-                    pkixParams.addAdditionalStore(X509Store.getInstance(
+-                        "ATTRIBUTECERTIFICATE/LDAP", params, "BC"));
+-                    pkixParams.addAdditionalStore(X509Store.getInstance(
+-                        "CERTIFICATEPAIR/LDAP", params, "BC"));
+-                }
++                // BEGIN android-removed
++                // if (location.startsWith("ldap://"))
++                // {
++                //     // ldap://directory.d-trust.net/CN=D-TRUST
++                //     // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
++                //     // skip "ldap://"
++                //     location = location.substring(7);
++                //     // after first / baseDN starts
++                //     String base = null;
++                //     String url = null;
++                //     if (location.indexOf("/") != -1)
++                //     {
++                //         base = location.substring(location.indexOf("/"));
++                //         // URL
++                //         url = "ldap://"
++                //             + location.substring(0, location.indexOf("/"));
++                //     }
++                //     else
++                //     {
++                //         url = "ldap://" + location;
++                //     }
++                //     // use all purpose parameters
++                //     X509LDAPCertStoreParameters params = new X509LDAPCertStoreParameters.Builder(
++                //         url, base).build();
++                //     pkixParams.addAdditionalStore(X509Store.getInstance(
++                //         "CERTIFICATE/LDAP", params, "BC"));
++                //     pkixParams.addAdditionalStore(X509Store.getInstance(
++                //         "CRL/LDAP", params, "BC"));
++                //     pkixParams.addAdditionalStore(X509Store.getInstance(
++                //         "ATTRIBUTECERTIFICATE/LDAP", params, "BC"));
++                //     pkixParams.addAdditionalStore(X509Store.getInstance(
++                //         "CERTIFICATEPAIR/LDAP", params, "BC"));
++                // }
++                // END android-removed
+             }
+             catch (Exception e)
+             {
+@@ -819,35 +875,37 @@
+         return certs;
+     }
+ 
+-    protected static Collection findCertificates(X509AttributeCertStoreSelector certSelect,
+-                                                 List certStores)
+-    throws AnnotatedException
+-    {
+-        Set certs = new HashSet();
+-        Iterator iter = certStores.iterator();
+-
+-        while (iter.hasNext())
+-        {
+-            Object obj = iter.next();
+-
+-            if (obj instanceof X509Store)
+-            {
+-                X509Store certStore = (X509Store)obj;
+-                try
+-                {
+-                    certs.addAll(certStore.getMatches(certSelect));
+-                }
+-                catch (StoreException e)
+-                {
+-                    throw
+-
+-                        new AnnotatedException(
+-                            "Problem while picking certificates from X.509 store.", e);
+-                }
+-            }
+-        }
+-        return certs;
+-    }
++    // BEGIN android-removed
++    // protected static Collection findCertificates(X509AttributeCertStoreSelector certSelect,
++    //                                              List certStores)
++    // throws AnnotatedException
++    // {
++    //     Set certs = new HashSet();
++    //     Iterator iter = certStores.iterator();
++    //
++    //     while (iter.hasNext())
++    //     {
++    //         Object obj = iter.next();
++    //
++    //         if (obj instanceof X509Store)
++    //         {
++    //             X509Store certStore = (X509Store)obj;
++    //             try
++    //             {
++    //                 certs.addAll(certStore.getMatches(certSelect));
++    //             }
++    //             catch (StoreException e)
++    //             {
++    //                 throw
++    //
++    //                     new AnnotatedException(
++    //                         "Problem while picking certificates from X.509 store.", e);
++    //             }
++    //         }
++    //     }
++    //     return certs;
++    // }
++    // END android-removed
+ 
+     protected static void addAdditionalStoresFromCRLDistributionPoint(
+         CRLDistPoint crldp, ExtendedPKIXParameters pkixParams)
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEBlockCipher.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEBlockCipher.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEBlockCipher.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEBlockCipher.java	2010-06-25 23:17:26.000000000 +0000
+@@ -6,23 +6,29 @@
+ import org.bouncycastle.crypto.DataLengthException;
+ import org.bouncycastle.crypto.InvalidCipherTextException;
+ import org.bouncycastle.crypto.engines.AESFastEngine;
+-import org.bouncycastle.crypto.engines.BlowfishEngine;
+-import org.bouncycastle.crypto.engines.CAST5Engine;
+-import org.bouncycastle.crypto.engines.CAST6Engine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.BlowfishEngine;
++// import org.bouncycastle.crypto.engines.CAST5Engine;
++// import org.bouncycastle.crypto.engines.CAST6Engine;
++// END android-removed
+ import org.bouncycastle.crypto.engines.DESEngine;
+ import org.bouncycastle.crypto.engines.DESedeEngine;
+-import org.bouncycastle.crypto.engines.GOST28147Engine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.GOST28147Engine;
++// END android-removed
+ import org.bouncycastle.crypto.engines.RC2Engine;
+ import org.bouncycastle.crypto.engines.RC532Engine;
+ import org.bouncycastle.crypto.engines.RC564Engine;
+-import org.bouncycastle.crypto.engines.RC6Engine;
+-import org.bouncycastle.crypto.engines.RijndaelEngine;
+-import org.bouncycastle.crypto.engines.SEEDEngine;
+-import org.bouncycastle.crypto.engines.SerpentEngine;
+-import org.bouncycastle.crypto.engines.SkipjackEngine;
+-import org.bouncycastle.crypto.engines.TEAEngine;
+-import org.bouncycastle.crypto.engines.TwofishEngine;
+-import org.bouncycastle.crypto.engines.XTEAEngine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.RC6Engine;
++// import org.bouncycastle.crypto.engines.RijndaelEngine;
++// import org.bouncycastle.crypto.engines.SEEDEngine;
++// import org.bouncycastle.crypto.engines.SerpentEngine;
++// import org.bouncycastle.crypto.engines.SkipjackEngine;
++// import org.bouncycastle.crypto.engines.TEAEngine;
++// import org.bouncycastle.crypto.engines.TwofishEngine;
++// import org.bouncycastle.crypto.engines.XTEAEngine;
++// END android-removed
+ import org.bouncycastle.crypto.modes.AEADBlockCipher;
+ import org.bouncycastle.crypto.modes.CBCBlockCipher;
+ import org.bouncycastle.crypto.modes.CCMBlockCipher;
+@@ -32,8 +38,10 @@
+ import org.bouncycastle.crypto.modes.GCMBlockCipher;
+ import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+ import org.bouncycastle.crypto.modes.OFBBlockCipher;
+-import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
+-import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
++// import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
++// END android-removed
+ import org.bouncycastle.crypto.modes.SICBlockCipher;
+ import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+ import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
+@@ -45,10 +53,14 @@
+ import org.bouncycastle.crypto.params.KeyParameter;
+ import org.bouncycastle.crypto.params.ParametersWithIV;
+ import org.bouncycastle.crypto.params.ParametersWithRandom;
+-import org.bouncycastle.crypto.params.ParametersWithSBox;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.params.ParametersWithSBox;
++// END android-removed
+ import org.bouncycastle.crypto.params.RC2Parameters;
+ import org.bouncycastle.crypto.params.RC5Parameters;
+-import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
++// BEGIN android-removed
++// import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
++// END android-removed
+ import org.bouncycastle.util.Strings;
+ 
+ import javax.crypto.BadPaddingException;
+@@ -82,7 +94,7 @@
+                                         RC5ParameterSpec.class,
+                                         IvParameterSpec.class,
+                                         PBEParameterSpec.class,
+-                                        GOST28147ParameterSpec.class
++                                        //GOST28147ParameterSpec.class
+                                     };
+  
+     private BlockCipher             baseEngine;
+@@ -237,20 +249,22 @@
+                         new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+             }
+         }
+-        else if (modeName.startsWith("PGP"))
+-        {
+-            boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
+-
+-            ivLength = baseEngine.getBlockSize();
+-            cipher = new BufferedGenericBlockCipher(
+-                new PGPCFBBlockCipher(baseEngine, inlineIV));
+-        }
+-        else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
+-        {
+-            ivLength = 0;
+-            cipher = new BufferedGenericBlockCipher(
+-                new OpenPGPCFBBlockCipher(baseEngine));
+-        }
++        // BEGIN android-removed
++        // else if (modeName.startsWith("PGP"))
++        // {
++        //     boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
++        //
++        //     ivLength = baseEngine.getBlockSize();
++        //     cipher = new BufferedGenericBlockCipher(
++        //         new PGPCFBBlockCipher(baseEngine, inlineIV));
++        // }
++        // else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
++        // {
++        //     ivLength = 0;
++        //     cipher = new BufferedGenericBlockCipher(
++        //         new OpenPGPCFBBlockCipher(baseEngine));
++        // }
++        // END android-removed
+         else if (modeName.startsWith("SIC"))
+         {
+             ivLength = baseEngine.getBlockSize();
+@@ -448,19 +462,21 @@
+                 param = new KeyParameter(key.getEncoded());
+             }
+         }
+-        else if (params instanceof GOST28147ParameterSpec)
+-        {
+-            GOST28147ParameterSpec    gost28147Param = (GOST28147ParameterSpec)params;
+-
+-            param = new ParametersWithSBox(
+-                       new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+-
+-            if (gost28147Param.getIV() != null && ivLength != 0)
+-            {
+-                param = new ParametersWithIV(param, gost28147Param.getIV());
+-                ivParam = (ParametersWithIV)param;
+-            }
+-        }
++        // BEGIN android-removed
++        // else if (params instanceof GOST28147ParameterSpec)
++        // {
++        //     GOST28147ParameterSpec    gost28147Param = (GOST28147ParameterSpec)params;
++        //
++        //     param = new ParametersWithSBox(
++        //                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
++        //
++        //     if (gost28147Param.getIV() != null && ivLength != 0)
++        //     {
++        //         param = new ParametersWithIV(param, gost28147Param.getIV());
++        //         ivParam = (ParametersWithIV)param;
++        //     }
++        // }
++        // END android-removed
+         else if (params instanceof RC2ParameterSpec)
+         {
+             RC2ParameterSpec    rc2Param = (RC2ParameterSpec)params;
+@@ -708,10 +724,21 @@
+         int     inputLen,
+         byte[]  output,
+         int     outputOffset) 
+-        throws IllegalBlockSizeException, BadPaddingException
++        throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
+     {
++        // BEGIN android-note
++        // added ShortBufferException to the throws statement
++        // END android-note
+         int     len = 0;
+ 
++        // BEGIN android-added
++        int outputLen = cipher.getOutputSize(inputLen);
++
++        if (outputLen + outputOffset > output.length) {
++            throw new ShortBufferException("need at least " + outputLen + " bytes");
++        }
++        // BEGIN android-added
++
+         if (inputLen != 0)
+         {
+                 len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+@@ -753,17 +780,19 @@
+         }
+     }
+ 
+-    /**
+-     * DESCBC
+-     */
+-    static public class DESCBC
+-        extends JCEBlockCipher
+-    {
+-        public DESCBC()
+-        {
+-            super(new CBCBlockCipher(new DESEngine()), 64);
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * DESCBC
++    //  */
++    // static public class DESCBC
++    //     extends JCEBlockCipher
++    // {
++    //     public DESCBC()
++    //     {
++    //         super(new CBCBlockCipher(new DESEngine()), 64);
++    //     }
++    // }
++    // END android-removed
+ 
+     /**
+      * DESede
+@@ -777,292 +806,294 @@
+         }
+     }
+ 
+-    /**
+-     * DESedeCBC
+-     */
+-    static public class DESedeCBC
+-        extends JCEBlockCipher
+-    {
+-        public DESedeCBC()
+-        {
+-            super(new CBCBlockCipher(new DESedeEngine()), 64);
+-        }
+-    }
+-
+-    /**
+-     *  GOST28147
+-     */
+-    static public class GOST28147
+-        extends JCEBlockCipher
+-    {
+-        public GOST28147()
+-        {
+-            super(new GOST28147Engine());
+-        }
+-    }
+-    
+-    static public class GOST28147cbc
+-        extends JCEBlockCipher
+-    {
+-        public GOST28147cbc()
+-        {
+-            super(new CBCBlockCipher(new GOST28147Engine()), 64);
+-        }
+-    }
+-    
+-    /**
+-     * SKIPJACK
+-     */
+-    static public class Skipjack
+-        extends JCEBlockCipher
+-    {
+-        public Skipjack()
+-        {
+-            super(new SkipjackEngine());
+-        }
+-    }
+-
+-    /**
+-     * Blowfish
+-     */
+-    static public class Blowfish
+-        extends JCEBlockCipher
+-    {
+-        public Blowfish()
+-        {
+-            super(new BlowfishEngine());
+-        }
+-    }
+-
+-    /**
+-     * Blowfish CBC
+-     */
+-    static public class BlowfishCBC
+-        extends JCEBlockCipher
+-    {
+-        public BlowfishCBC()
+-        {
+-            super(new CBCBlockCipher(new BlowfishEngine()), 64);
+-        }
+-    }
+-
+-    /**
+-     * Twofish
+-     */
+-    static public class Twofish
+-        extends JCEBlockCipher
+-    {
+-        public Twofish()
+-        {
+-            super(new TwofishEngine());
+-        }
+-    }
+-
+-    /**
+-     * RC2
+-     */
+-    static public class RC2
+-        extends JCEBlockCipher
+-    {
+-        public RC2()
+-        {
+-            super(new RC2Engine());
+-        }
+-    }
+-
+-    /**
+-     * RC2CBC
+-     */
+-    static public class RC2CBC
+-        extends JCEBlockCipher
+-    {
+-        public RC2CBC()
+-        {
+-            super(new CBCBlockCipher(new RC2Engine()), 64);
+-        }
+-    }
+-
+-    /**
+-     * RC5
+-     */
+-    static public class RC5
+-        extends JCEBlockCipher
+-    {
+-        public RC5()
+-        {
+-            super(new RC532Engine());
+-        }
+-    }
+-
+-    /**
+-     * RC564
+-     */
+-    static public class RC564
+-        extends JCEBlockCipher
+-    {
+-        public RC564()
+-        {
+-            super(new RC564Engine());
+-        }
+-    }
+-
+-    /**
+-     * RC6
+-     */
+-    static public class RC6
+-        extends JCEBlockCipher
+-    {
+-        public RC6()
+-        {
+-            super(new RC6Engine());
+-        }
+-    }
+-
+-    /**
+-     * AES
+-     */
+-    static public class AES
+-        extends JCEBlockCipher
+-    {
+-        public AES()
+-        {
+-            super(new AESFastEngine());
+-        }
+-    }
+-
+-    /**
+-     * AESCBC
+-     */
+-    static public class AESCBC
+-        extends JCEBlockCipher
+-    {
+-        public AESCBC()
+-        {
+-            super(new CBCBlockCipher(new AESFastEngine()), 128);
+-        }
+-    }
+-
+-    /**
+-     * AESCFB
+-     */
+-    static public class AESCFB
+-        extends JCEBlockCipher
+-    {
+-        public AESCFB()
+-        {
+-            super(new CFBBlockCipher(new AESFastEngine(), 128), 128);
+-        }
+-    }
+-
+-    /**
+-     * AESOFB
+-     */
+-    static public class AESOFB
+-        extends JCEBlockCipher
+-    {
+-        public AESOFB()
+-        {
+-            super(new OFBBlockCipher(new AESFastEngine(), 128), 128);
+-        }
+-    }
+-
+-    /**
+-     * Rijndael
+-     */
+-    static public class Rijndael
+-        extends JCEBlockCipher
+-    {
+-        public Rijndael()
+-        {
+-            super(new RijndaelEngine());
+-        }
+-    }
+-
+-    /**
+-     * Serpent
+-     */
+-    static public class Serpent
+-        extends JCEBlockCipher
+-    {
+-        public Serpent()
+-        {
+-            super(new SerpentEngine());
+-        }
+-    }
+-
+-
+-    
+-    /**
+-     * CAST5
+-     */
+-    static public class CAST5
+-        extends JCEBlockCipher
+-    {
+-        public CAST5()
+-        {
+-            super(new CAST5Engine());
+-        }
+-    }
+-
+-    /**
+-     * CAST5 CBC
+-     */
+-    static public class CAST5CBC
+-        extends JCEBlockCipher
+-    {
+-        public CAST5CBC()
+-        {
+-            super(new CBCBlockCipher(new CAST5Engine()), 64);
+-        }
+-    }
+-
+-    /**
+-     * CAST6
+-     */
+-    static public class CAST6
+-        extends JCEBlockCipher
+-    {
+-        public CAST6()
+-        {
+-            super(new CAST6Engine());
+-        }
+-    }
+-
+-    /**
+-     * TEA
+-     */
+-    static public class TEA
+-        extends JCEBlockCipher
+-    {
+-        public TEA()
+-        {
+-            super(new TEAEngine());
+-        }
+-    }
+-
+-    /**
+-     * XTEA
+-     */
+-    static public class XTEA
+-        extends JCEBlockCipher
+-    {
+-        public XTEA()
+-        {
+-            super(new XTEAEngine());
+-        }
+-    }
+-
+-    /**
+-     * SEED
+-     */
+-    static public class SEED
+-        extends JCEBlockCipher
+-    {
+-        public SEED()
+-        {
+-            super(new SEEDEngine());
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * DESedeCBC
++    //  */
++    // static public class DESedeCBC
++    //     extends JCEBlockCipher
++    // {
++    //     public DESedeCBC()
++    //     {
++    //         super(new CBCBlockCipher(new DESedeEngine()), 64);
++    //     }
++    // }
++    //
++    // /**
++    //  *  GOST28147
++    //  */
++    // static public class GOST28147
++    //     extends JCEBlockCipher
++    // {
++    //     public GOST28147()
++    //     {
++    //         super(new GOST28147Engine());
++    //     }
++    // }
++    //    
++    // static public class GOST28147cbc
++    //     extends JCEBlockCipher
++    // {
++    //     public GOST28147cbc()
++    //     {
++    //         super(new CBCBlockCipher(new GOST28147Engine()), 64);
++    //     }
++    // }
++    //
++    // /**
++    //  * SKIPJACK
++    //  */
++    // static public class Skipjack
++    //     extends JCEBlockCipher
++    // {
++    //     public Skipjack()
++    //     {
++    //         super(new SkipjackEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * Blowfish
++    //  */
++    // static public class Blowfish
++    //     extends JCEBlockCipher
++    // {
++    //     public Blowfish()
++    //     {
++    //         super(new BlowfishEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * Blowfish CBC
++    //  */
++    // static public class BlowfishCBC
++    //     extends JCEBlockCipher
++    // {
++    //     public BlowfishCBC()
++    //     {
++    //         super(new CBCBlockCipher(new BlowfishEngine()), 64);
++    //     }
++    // }
++    //
++    // /**
++    //  * Twofish
++    //  */
++    // static public class Twofish
++    //     extends JCEBlockCipher
++    // {
++    //     public Twofish()
++    //     {
++    //         super(new TwofishEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC2
++    //  */
++    // static public class RC2
++    //     extends JCEBlockCipher
++    // {
++    //     public RC2()
++    //     {
++    //         super(new RC2Engine());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC2CBC
++    //  */
++    // static public class RC2CBC
++    //     extends JCEBlockCipher
++    // {
++    //     public RC2CBC()
++    //     {
++    //         super(new CBCBlockCipher(new RC2Engine()), 64);
++    //     }
++    // }
++    //
++    // /**
++    //  * RC5
++    //  */
++    // static public class RC5
++    //     extends JCEBlockCipher
++    // {
++    //     public RC5()
++    //     {
++    //         super(new RC532Engine());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC564
++    //  */
++    // static public class RC564
++    //     extends JCEBlockCipher
++    // {
++    //     public RC564()
++    //     {
++    //         super(new RC564Engine());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC6
++    //  */
++    // static public class RC6
++    //     extends JCEBlockCipher
++    // {
++    //     public RC6()
++    //     {
++    //         super(new RC6Engine());
++    //     }
++    // }
++    //
++    // /**
++    //  * AES
++    //  */
++    // static public class AES
++    //     extends JCEBlockCipher
++    // {
++    //     public AES()
++    //     {
++    //         super(new AESFastEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * AESCBC
++    //  */
++    // static public class AESCBC
++    //     extends JCEBlockCipher
++    // {
++    //     public AESCBC()
++    //     {
++    //         super(new CBCBlockCipher(new AESFastEngine()), 128);
++    //     }
++    // }
++    //
++    // /**
++    //  * AESCFB
++    //  */
++    // static public class AESCFB
++    //     extends JCEBlockCipher
++    // {
++    //     public AESCFB()
++    //     {
++    //         super(new CFBBlockCipher(new AESFastEngine(), 128), 128);
++    //     }
++    // }
++    //
++    // /**
++    //  * AESOFB
++    //  */
++    // static public class AESOFB
++    //     extends JCEBlockCipher
++    // {
++    //     public AESOFB()
++    //     {
++    //         super(new OFBBlockCipher(new AESFastEngine(), 128), 128);
++    //     }
++    // }
++    //
++    // /**
++    //  * Rijndael
++    //  */
++    // static public class Rijndael
++    //     extends JCEBlockCipher
++    // {
++    //     public Rijndael()
++    //     {
++    //         super(new RijndaelEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * Serpent
++    //  */
++    // static public class Serpent
++    //     extends JCEBlockCipher
++    // {
++    //     public Serpent()
++    //     {
++    //         super(new SerpentEngine());
++    //     }
++    // }
++    //
++    //
++    //
++    // /**
++    //  * CAST5
++    //  */
++    // static public class CAST5
++    //     extends JCEBlockCipher
++    // {
++    //     public CAST5()
++    //     {
++    //         super(new CAST5Engine());
++    //     }
++    // }
++    //
++    // /**
++    //  * CAST5 CBC
++    //  */
++    // static public class CAST5CBC
++    //     extends JCEBlockCipher
++    // {
++    //     public CAST5CBC()
++    //     {
++    //         super(new CBCBlockCipher(new CAST5Engine()), 64);
++    //     }
++    // }
++    //
++    // /**
++    //  * CAST6
++    //  */
++    // static public class CAST6
++    //     extends JCEBlockCipher
++    // {
++    //     public CAST6()
++    //     {
++    //         super(new CAST6Engine());
++    //     }
++    // }
++    // 
++    // /**
++    //  * TEA
++    //  */
++    // static public class TEA
++    //     extends JCEBlockCipher
++    // {
++    //     public TEA()
++    //     {
++    //         super(new TEAEngine());
++    //     }
++    // }
++    //
++    // /**
++    //  * XTEA
++    //  */
++    // static public class XTEA
++    //     extends JCEBlockCipher
++    // {
++    //     public XTEA()
++    //     {
++    //         super(new XTEAEngine());
++    //     }
++    // }
++    // 
++    // /**
++    //  * SEED
++    //  */
++    // static public class SEED
++    //     extends JCEBlockCipher
++    // {
++    //     public SEED()
++    //     {
++    //         super(new SEEDEngine());
++    //     }
++    // }
++    // END android-removed
+ 
+     /**
+      * PBEWithMD5AndDES
+@@ -1076,41 +1107,31 @@
+         }
+     }
+ 
+-    /**
+-     * PBEWithMD5AndRC2
+-     */
+-    static public class PBEWithMD5AndRC2
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithMD5AndRC2()
+-        {
+-            super(new CBCBlockCipher(new RC2Engine()));
+-        }
+-    }
+-
+-    /**
+-     * PBEWithSHA1AndDES
+-     */
+-    static public class PBEWithSHA1AndDES
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHA1AndDES()
+-        {
+-            super(new CBCBlockCipher(new DESEngine()));
+-        }
+-    }
+-
+-    /**
+-     * PBEWithSHA1AndRC2
+-     */
+-    static public class PBEWithSHA1AndRC2
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHA1AndRC2()
+-        {
+-            super(new CBCBlockCipher(new RC2Engine()));
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * PBEWithMD5AndRC2
++    //  */
++    // static public class PBEWithMD5AndRC2
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithMD5AndRC2()
++    //     {
++    //         super(new CBCBlockCipher(new RC2Engine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * PBEWithSHA1AndDES
++    //  */
++    // static public class PBEWithSHA1AndDES
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithSHA1AndDES()
++    //     {
++    //         super(new CBCBlockCipher(new DESEngine()));
++    //     }
++    // }
++    // END android-removed
+ 
+     /**
+      * PBEWithSHAAnd3-KeyTripleDES-CBC
+@@ -1124,54 +1145,20 @@
+         }
+     }
+ 
+-    /**
+-     * PBEWithSHAAnd2-KeyTripleDES-CBC
+-     */
+-    static public class PBEWithSHAAndDES2Key
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHAAndDES2Key()
+-        {
+-            super(new CBCBlockCipher(new DESedeEngine()));
+-        }
+-    }
+-
+-    /**
+-     * PBEWithSHAAnd128BitRC2-CBC
+-     */
+-    static public class PBEWithSHAAnd128BitRC2
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHAAnd128BitRC2()
+-        {
+-            super(new CBCBlockCipher(new RC2Engine()));
+-        }
+-    }
+-
+-    /**
+-     * PBEWithSHAAnd40BitRC2-CBC
+-     */
+-    static public class PBEWithSHAAnd40BitRC2
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHAAnd40BitRC2()
+-        {
+-            super(new CBCBlockCipher(new RC2Engine()));
+-        }
+-    }
+-
+-    /**
+-     * PBEWithSHAAndTwofish-CBC
+-     */
+-    static public class PBEWithSHAAndTwofish
+-        extends JCEBlockCipher
+-    {
+-        public PBEWithSHAAndTwofish()
+-        {
+-            super(new CBCBlockCipher(new TwofishEngine()));
+-        }
+-    }
+-
++    // BEGIN android-removed
++    // /**
++    //  * PBEWithSHAAnd2-KeyTripleDES-CBC
++    //  */
++    // static public class PBEWithSHAAndDES2Key
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithSHAAndDES2Key()
++    //     {
++    //         super(new CBCBlockCipher(new DESedeEngine()));
++    //     }
++    // }
++    // END android-removed
++    
+     /**
+      * PBEWithAES-CBC
+      */
+@@ -1183,6 +1170,44 @@
+             super(new CBCBlockCipher(new AESFastEngine()));
+         }
+     }
++    
++    // BEGIN android-removed
++    // /**
++    //  * PBEWITHSHAAND40BITRC2-CBC
++    //  */
++    // static public class PBEWithSHAAnd40BitRC2
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithSHAAnd40BitRC2()
++    //     {
++    //         super(new CBCBlockCipher(new RC2Engine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * PBEWithSHAAndTwofish-CBC
++    //  */
++    // static public class PBEWithSHAAndTwofish
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithSHAAndTwofish()
++    //     {
++    //         super(new CBCBlockCipher(new TwofishEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * PBEWithAES-CBC
++    //  */
++    // static public class PBEWithAESCBC
++    //     extends JCEBlockCipher
++    // {
++    //     public PBEWithAESCBC()
++    //     {
++    //         super(new CBCBlockCipher(new AESFastEngine()));
++    //     }
++    // }
++    // END android-removed
+ 
+     static private interface GenericBlockCipher
+     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java	2010-06-25 23:17:26.000000000 +0000
+@@ -37,9 +37,11 @@
+ 
+     static
+     {
+-        Integer i64 = new Integer(64);
+-        Integer i192 = new Integer(192);
+-        Integer i128 = new Integer(128);
++        // BEGIN android-changed
++        Integer i64 = Integer.valueOf(64);
++        Integer i192 = Integer.valueOf(192);
++        Integer i128 = Integer.valueOf(128);
++        // END android-changed
+ 
+         algorithms.put("DES", i64);
+         algorithms.put("DESEDE", i192);
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEDigestUtil.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEDigestUtil.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEDigestUtil.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEDigestUtil.java	2010-06-25 23:17:26.000000000 +0000
+@@ -12,7 +12,9 @@
+ import org.bouncycastle.crypto.Digest;
+ import org.bouncycastle.crypto.digests.MD5Digest;
+ import org.bouncycastle.crypto.digests.SHA1Digest;
+-import org.bouncycastle.crypto.digests.SHA224Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.SHA224Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA256Digest;
+ import org.bouncycastle.crypto.digests.SHA384Digest;
+ import org.bouncycastle.crypto.digests.SHA512Digest;
+@@ -22,7 +24,9 @@
+ {
+     private static Set md5 = new HashSet();
+     private static Set sha1 = new HashSet();
+-    private static Set sha224 = new HashSet();
++    // BEGIN android-removed
++    // private static Set sha224 = new HashSet();
++    // END android-removed
+     private static Set sha256 = new HashSet();
+     private static Set sha384 = new HashSet();
+     private static Set sha512 = new HashSet();
+@@ -38,9 +42,11 @@
+         sha1.add("SHA-1");
+         sha1.add(OIWObjectIdentifiers.idSHA1.getId());
+         
+-        sha224.add("SHA224");
+-        sha224.add("SHA-224");
+-        sha224.add(NISTObjectIdentifiers.id_sha224.getId());
++        // BEGIN android-removed
++        // sha224.add("SHA224");
++        // sha224.add("SHA-224");
++        // sha224.add(NISTObjectIdentifiers.id_sha224.getId());
++        // END android-removed
+         
+         sha256.add("SHA256");
+         sha256.add("SHA-256");
+@@ -61,9 +67,11 @@
+         oids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
+         oids.put(OIWObjectIdentifiers.idSHA1.getId(), OIWObjectIdentifiers.idSHA1);
+         
+-        oids.put("SHA224", NISTObjectIdentifiers.id_sha224);
+-        oids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
+-        oids.put(NISTObjectIdentifiers.id_sha224.getId(), NISTObjectIdentifiers.id_sha224);
++        // BEGIN android-removed
++        // oids.put("SHA224", NISTObjectIdentifiers.id_sha224);
++        // oids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
++        // oids.put(NISTObjectIdentifiers.id_sha224.getId(), NISTObjectIdentifiers.id_sha224);
++        // END android-removed
+         
+         oids.put("SHA256", NISTObjectIdentifiers.id_sha256);
+         oids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
+@@ -91,10 +99,12 @@
+         {
+             return new MD5Digest();
+         }
+-        if (sha224.contains(digestName))
+-        {
+-            return new SHA224Digest();
+-        }
++        // BEGIN android-removed
++        // if (sha224.contains(digestName))
++        // {
++        //     return new SHA224Digest();
++        // }
++        // END android-removed
+         if (sha256.contains(digestName))
+         {
+             return new SHA256Digest();
+@@ -116,7 +126,9 @@
+         String digest2)
+     {
+         return (sha1.contains(digest1) && sha1.contains(digest2))
+-            || (sha224.contains(digest1) && sha224.contains(digest2))
++            // BEGIN android-removed
++            // || (sha224.contains(digest1) && sha224.contains(digest2))
++            // END android-removed
+             || (sha256.contains(digest1) && sha256.contains(digest2))
+             || (sha384.contains(digest1) && sha384.contains(digest2))
+             || (sha512.contains(digest1) && sha512.contains(digest2))
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEKeyGenerator.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEKeyGenerator.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEKeyGenerator.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEKeyGenerator.java	2010-06-25 23:17:26.000000000 +0000
+@@ -145,274 +145,278 @@
+         }
+     }
+     
+-    /**
+-     * generate a desEDE key in the a-b-c format.
+-     */
+-    public static class DESede3
+-        extends JCEKeyGenerator
+-    {
+-        public DESede3()
+-        {
+-            super("DESede3", 192, new DESedeKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * SKIPJACK
+-     */
+-    public static class Skipjack
+-        extends JCEKeyGenerator
+-    {
+-        public Skipjack()
+-        {
+-            super("SKIPJACK", 80, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * Blowfish
+-     */
+-    public static class Blowfish
+-        extends JCEKeyGenerator
+-    {
+-        public Blowfish()
+-        {
+-            super("Blowfish", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * Twofish
+-     */
+-    public static class Twofish
+-        extends JCEKeyGenerator
+-    {
+-        public Twofish()
+-        {
+-            super("Twofish", 256, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RC2
+-     */
+-    public static class RC2
+-        extends JCEKeyGenerator
+-    {
+-        public RC2()
+-        {
+-            super("RC2", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RC4
+-     */
+-    public static class RC4
+-        extends JCEKeyGenerator
+-    {
+-        public RC4()
+-        {
+-            super("RC4", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RC5
+-     */
+-    public static class RC5
+-        extends JCEKeyGenerator
+-    {
+-        public RC5()
+-        {
+-            super("RC5", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RC5
+-     */
+-    public static class RC564
+-        extends JCEKeyGenerator
+-    {
+-        public RC564()
+-        {
+-            super("RC5-64", 256, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RC6
+-     */
+-    public static class RC6
+-        extends JCEKeyGenerator
+-    {
+-        public RC6()
+-        {
+-            super("RC6", 256, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * GOST28147
+-     */
+-    public static class GOST28147
+-        extends JCEKeyGenerator
+-    {
+-        public GOST28147()
+-        {
+-            super("GOST28147", 256, new CipherKeyGenerator());
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * generate a desEDE key in the a-b-c format.
++    //  */
++    // public static class DESede3
++    //     extends JCEKeyGenerator
++    // {
++    //     public DESede3()
++    //     {
++    //         super("DESede3", 192, new DESedeKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * SKIPJACK
++    //  */
++    // public static class Skipjack
++    //     extends JCEKeyGenerator
++    // {
++    //     public Skipjack()
++    //     {
++    //         super("SKIPJACK", 80, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * Blowfish
++    //  */
++    // public static class Blowfish
++    //     extends JCEKeyGenerator
++    // {
++    //     public Blowfish()
++    //     {
++    //         super("Blowfish", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * Twofish
++    //  */
++    // public static class Twofish
++    //     extends JCEKeyGenerator
++    // {
++    //     public Twofish()
++    //     {
++    //         super("Twofish", 256, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC2
++    //  */
++    // public static class RC2
++    //     extends JCEKeyGenerator
++    // {
++    //     public RC2()
++    //     {
++    //         super("RC2", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC4
++    //  */
++    // public static class RC4
++    //     extends JCEKeyGenerator
++    // {
++    //     public RC4()
++    //     {
++    //         super("RC4", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC5
++    //  */
++    // public static class RC5
++    //     extends JCEKeyGenerator
++    // {
++    //     public RC5()
++    //     {
++    //         super("RC5", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC5
++    //  */
++    // public static class RC564
++    //     extends JCEKeyGenerator
++    // {
++    //     public RC564()
++    //     {
++    //         super("RC5-64", 256, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * RC6
++    //  */
++    // public static class RC6
++    //     extends JCEKeyGenerator
++    // {
++    //     public RC6()
++    //     {
++    //         super("RC6", 256, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * GOST28147
++    //  */
++    // public static class GOST28147
++    //     extends JCEKeyGenerator
++    // {
++    //     public GOST28147()
++    //     {
++    //         super("GOST28147", 256, new CipherKeyGenerator());
++    //     }
++    // }
+     
+-    /**
+-     * Rijndael
+-     */
+-    public static class Rijndael
+-        extends JCEKeyGenerator
+-    {
+-        public Rijndael()
+-        {
+-            super("Rijndael", 192, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * Serpent
+-     */
+-    public static class Serpent
+-        extends JCEKeyGenerator
+-    {
+-        public Serpent()
+-        {
+-            super("Serpent", 192, new CipherKeyGenerator());
+-        }
+-    }
+-    
+-
+-
+-    /**
+-     * CAST6
+-     */
+-    public static class CAST6
+-        extends JCEKeyGenerator
+-    {
+-        public CAST6()
+-        {
+-            super("CAST6", 256, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * TEA
+-     */
+-    public static class TEA
+-        extends JCEKeyGenerator
+-    {
+-        public TEA()
+-        {
+-            super("TEA", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * XTEA
+-     */
+-    public static class XTEA
+-        extends JCEKeyGenerator
+-    {
+-        public XTEA()
+-        {
+-            super("XTEA", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * Salsa20
+-     */
+-    public static class Salsa20
+-        extends JCEKeyGenerator
+-    {
+-        public Salsa20()
+-        {
+-            super("Salsa20", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * HC128
+-     */
+-    public static class HC128
+-        extends JCEKeyGenerator
+-    {
+-        public HC128()
+-        {
+-            super("HC128", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * HC256
+-     */
+-    public static class HC256
+-        extends JCEKeyGenerator
+-    {
+-        public HC256()
+-        {
+-            super("HC256", 256, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * VMPC
+-     */
+-    public static class VMPC
+-        extends JCEKeyGenerator
+-    {
+-        public VMPC()
+-        {
+-            super("VMPC", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * VMPC-KSA3
+-     */
+-    public static class VMPCKSA3
+-        extends JCEKeyGenerator
+-    {
+-        public VMPCKSA3()
+-        {
+-            super("VMPC-KSA3", 128, new CipherKeyGenerator());
+-        }
+-    }
++    // /**
++    //  * Rijndael
++    //  */
++    // public static class Rijndael
++    //     extends JCEKeyGenerator
++    // {
++    //     public Rijndael()
++    //     {
++    //         super("Rijndael", 192, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * Serpent
++    //  */
++    // public static class Serpent
++    //     extends JCEKeyGenerator
++    // {
++    //     public Serpent()
++    //     {
++    //         super("Serpent", 192, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    //
++    //
++    // /**
++    //  * CAST6
++    //  */
++    // public static class CAST6
++    //     extends JCEKeyGenerator
++    // {
++    //     public CAST6()
++    //     {
++    //         super("CAST6", 256, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * TEA
++    //  */
++    // public static class TEA
++    //     extends JCEKeyGenerator
++    // {
++    //     public TEA()
++    //     {
++    //         super("TEA", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * XTEA
++    //  */
++    // public static class XTEA
++    //     extends JCEKeyGenerator
++    // {
++    //     public XTEA()
++    //     {
++    //         super("XTEA", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * Salsa20
++    //  */
++    // public static class Salsa20
++    //     extends JCEKeyGenerator
++    // {
++    //     public Salsa20()
++    //     {
++    //         super("Salsa20", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * HC128
++    //  */
++    // public static class HC128
++    //     extends JCEKeyGenerator
++    // {
++    //     public HC128()
++    //     {
++    //         super("HC128", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * HC256
++    //  */
++    // public static class HC256
++    //     extends JCEKeyGenerator
++    // {
++    //     public HC256()
++    //     {
++    //         super("HC256", 256, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * VMPC
++    //  */
++    // public static class VMPC
++    //     extends JCEKeyGenerator
++    // {
++    //     public VMPC()
++    //     {
++    //         super("VMPC", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    // /**
++    //  * VMPC-KSA3
++    //  */
++    // public static class VMPCKSA3
++    //     extends JCEKeyGenerator
++    // {
++    //     public VMPCKSA3()
++    //     {
++    //         super("VMPC-KSA3", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    // END android-removed
+ 
+     // HMAC Related secret keys..
+   
+-    /**
+-     * MD2HMAC
+-     */
+-    public static class MD2HMAC
+-        extends JCEKeyGenerator
+-    {
+-        public MD2HMAC()
+-        {
+-            super("HMACMD2", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-
+-    /**
+-     * MD4HMAC
+-     */
+-    public static class MD4HMAC
+-        extends JCEKeyGenerator
+-    {
+-        public MD4HMAC()
+-        {
+-            super("HMACMD4", 128, new CipherKeyGenerator());
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * MD2HMAC
++    //  */
++    // public static class MD2HMAC
++    //     extends JCEKeyGenerator
++    // {
++    //     public MD2HMAC()
++    //     {
++    //         super("HMACMD2", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    //
++    //
++    // /**
++    //  * MD4HMAC
++    //  */
++    // public static class MD4HMAC
++    //     extends JCEKeyGenerator
++    // {
++    //     public MD4HMAC()
++    //     {
++    //         super("HMACMD4", 128, new CipherKeyGenerator());
++    //     }
++    // }
++    // END android-removed
+ 
+     /**
+      * MD5HMAC
+@@ -427,29 +431,29 @@
+     }
+ 
+ 
+-    /**
+-     * RIPE128HMAC
+-     */
+-    public static class RIPEMD128HMAC
+-        extends JCEKeyGenerator
+-    {
+-        public RIPEMD128HMAC()
+-        {
+-            super("HMACRIPEMD128", 128, new CipherKeyGenerator());
+-        }
+-    }
+-
+-    /**
+-     * RIPE160HMAC
+-     */
+-    public static class RIPEMD160HMAC
+-        extends JCEKeyGenerator
+-    {
+-        public RIPEMD160HMAC()
+-        {
+-            super("HMACRIPEMD160", 160, new CipherKeyGenerator());
+-        }
+-    }
++    // /**
++    //  * RIPE128HMAC
++    //  */
++    // public static class RIPEMD128HMAC
++    //     extends JCEKeyGenerator
++    // {
++    //     public RIPEMD128HMAC()
++    //     {
++    //         super("HMACRIPEMD128", 128, new CipherKeyGenerator());
++    //     }
++    // }
++
++    // /**
++    //  * RIPE160HMAC
++    //  */
++    // public static class RIPEMD160HMAC
++    //     extends JCEKeyGenerator
++    // {
++    //     public RIPEMD160HMAC()
++    //     {
++    //         super("HMACRIPEMD160", 160, new CipherKeyGenerator());
++    //     }
++    // }
+ 
+ 
+     /**
+@@ -464,17 +468,19 @@
+         }
+     }
+ 
+-    /**
+-     * HMACSHA224
+-     */
+-    public static class HMACSHA224
+-        extends JCEKeyGenerator
+-    {
+-        public HMACSHA224()
+-        {
+-            super("HMACSHA224", 224, new CipherKeyGenerator());
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * HMACSHA224
++    //  */
++    // public static class HMACSHA224
++    //     extends JCEKeyGenerator
++    // {
++    //     public HMACSHA224()
++    //     {
++    //         super("HMACSHA224", 224, new CipherKeyGenerator());
++    //     }
++    // }
++    // END android-removed
+     
+     /**
+      * HMACSHA256
+@@ -512,15 +518,17 @@
+         }
+     }
+     
+-    /**
+-     * HMACTIGER
+-     */
+-    public static class HMACTIGER
+-        extends JCEKeyGenerator
+-    {
+-        public HMACTIGER()
+-        {
+-            super("HMACTIGER", 192, new CipherKeyGenerator());
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * HMACTIGER
++    //  */
++    // public static class HMACTIGER
++    //     extends JCEKeyGenerator
++    // {
++    //     public HMACTIGER()
++    //     {
++    //         super("HMACTIGER", 192, new CipherKeyGenerator());
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEMac.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEMac.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCEMac.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCEMac.java	2010-06-25 23:17:26.000000000 +0000
+@@ -2,29 +2,43 @@
+ 
+ import org.bouncycastle.crypto.CipherParameters;
+ import org.bouncycastle.crypto.Mac;
+-import org.bouncycastle.crypto.digests.MD2Digest;
+-import org.bouncycastle.crypto.digests.MD4Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.MD2Digest;
++// import org.bouncycastle.crypto.digests.MD4Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.MD5Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD160Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
++// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA1Digest;
+-import org.bouncycastle.crypto.digests.SHA224Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.SHA224Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA256Digest;
+ import org.bouncycastle.crypto.digests.SHA384Digest;
+ import org.bouncycastle.crypto.digests.SHA512Digest;
+-import org.bouncycastle.crypto.digests.TigerDigest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.TigerDigest;
++// END android-removed
+ import org.bouncycastle.crypto.engines.DESEngine;
+ import org.bouncycastle.crypto.engines.DESedeEngine;
+-import org.bouncycastle.crypto.engines.RC2Engine;
+-import org.bouncycastle.crypto.engines.RC532Engine;
+-import org.bouncycastle.crypto.engines.SkipjackEngine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.RC2Engine;
++// import org.bouncycastle.crypto.engines.RC532Engine;
++// import org.bouncycastle.crypto.engines.SkipjackEngine;
++// END android-removed
+ import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
+-import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
+-import org.bouncycastle.crypto.macs.GOST28147Mac;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
++// import org.bouncycastle.crypto.macs.GOST28147Mac;
++// END android-removed
+ import org.bouncycastle.crypto.macs.HMac;
+-import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
+-import org.bouncycastle.crypto.macs.OldHMac;
+-import org.bouncycastle.crypto.macs.VMPCMac;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
++// import org.bouncycastle.crypto.macs.OldHMac;
++// import org.bouncycastle.crypto.macs.VMPCMac;
++// END android-removed
+ import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+ import org.bouncycastle.crypto.params.KeyParameter;
+ import org.bouncycastle.crypto.params.ParametersWithIV;
+@@ -146,224 +160,226 @@
+      * the classes that extend directly off us.
+      */
+ 
+-    /**
+-     * DES
+-     */
+-    public static class DES
+-        extends JCEMac
+-    {
+-        public DES()
+-        {
+-            super(new CBCBlockCipherMac(new DESEngine()));
+-        }
+-    }
+-
+-    /**
+-     * DESede
+-     */
+-    public static class DESede
+-        extends JCEMac
+-    {
+-        public DESede()
+-        {
+-            super(new CBCBlockCipherMac(new DESedeEngine()));
+-        }
+-    }
+-
+-    /**
+-     * SKIPJACK
+-     */
+-    public static class Skipjack
+-        extends JCEMac
+-    {
+-        public Skipjack()
+-        {
+-            super(new CBCBlockCipherMac(new SkipjackEngine()));
+-        }
+-    }
+-
+-    /**
+-     * RC2
+-     */
+-    public static class RC2
+-        extends JCEMac
+-    {
+-        public RC2()
+-        {
+-            super(new CBCBlockCipherMac(new RC2Engine()));
+-        }
+-    }
+-
+-    /**
+-     * RC5
+-     */
+-    public static class RC5
+-        extends JCEMac
+-    {
+-        public RC5()
+-        {
+-            super(new CBCBlockCipherMac(new RC532Engine()));
+-        }
+-    }
+-
+-    /**
+-     * GOST28147
+-     */
+-    public static class GOST28147
+-        extends JCEMac
+-    {
+-        public GOST28147()
+-        {
+-            super(new GOST28147Mac());
+-        }
+-    }
+-
+-    /**
+-     * VMPC
+-     */
+-    public static class VMPC
+-        extends JCEMac
+-    {
+-        public VMPC()
+-        {
+-            super(new VMPCMac());
+-        }
+-    }
+-
+-    /**
+-     * DES
+-     */
+-    public static class DESCFB8
+-        extends JCEMac
+-    {
+-        public DESCFB8()
+-        {
+-            super(new CFBBlockCipherMac(new DESEngine()));
+-        }
+-    }
+-
+-    /**
+-     * DESede
+-     */
+-    public static class DESedeCFB8
+-        extends JCEMac
+-    {
+-        public DESedeCFB8()
+-        {
+-            super(new CFBBlockCipherMac(new DESedeEngine()));
+-        }
+-    }
+-
+-    /**
+-     * SKIPJACK
+-     */
+-    public static class SkipjackCFB8
+-        extends JCEMac
+-    {
+-        public SkipjackCFB8()
+-        {
+-            super(new CFBBlockCipherMac(new SkipjackEngine()));
+-        }
+-    }
+-
+-    /**
+-     * RC2CFB8
+-     */
+-    public static class RC2CFB8
+-        extends JCEMac
+-    {
+-        public RC2CFB8()
+-        {
+-            super(new CFBBlockCipherMac(new RC2Engine()));
+-        }
+-    }
+-
+-    /**
+-     * RC5CFB8
+-     */
+-    public static class RC5CFB8
+-        extends JCEMac
+-    {
+-        public RC5CFB8()
+-        {
+-            super(new CFBBlockCipherMac(new RC532Engine()));
+-        }
+-    }
+-    
++    // BEGIN android-removed
++    // /**
++    //  * DES
++    //  */
++    // public static class DES
++    //     extends JCEMac
++    // {
++    //     public DES()
++    //     {
++    //         super(new CBCBlockCipherMac(new DESEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * DESede
++    //  */
++    // public static class DESede
++    //     extends JCEMac
++    // {
++    //     public DESede()
++    //     {
++    //         super(new CBCBlockCipherMac(new DESedeEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * SKIPJACK
++    //  */
++    // public static class Skipjack
++    //     extends JCEMac
++    // {
++    //     public Skipjack()
++    //     {
++    //         super(new CBCBlockCipherMac(new SkipjackEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * RC2
++    //  */
++    // public static class RC2
++    //     extends JCEMac
++    // {
++    //     public RC2()
++    //     {
++    //         super(new CBCBlockCipherMac(new RC2Engine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * RC5
++    //  */
++    // public static class RC5
++    //     extends JCEMac
++    // {
++    //     public RC5()
++    //     {
++    //         super(new CBCBlockCipherMac(new RC532Engine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * GOST28147
++    //  */
++    // public static class GOST28147
++    //     extends JCEMac
++    // {
++    //     public GOST28147()
++    //     {
++    //         super(new GOST28147Mac());
++    //     }
++    // }
++    //
++    // /**
++    //  * VMPC
++    //  */
++    // public static class VMPC
++    //     extends JCEMac
++    // {
++    //     public VMPC()
++    //     {
++    //         super(new VMPCMac());
++    //     }
++    // }
++    //
++    // /**
++    //  * DES
++    //  */
++    // public static class DESCFB8
++    //     extends JCEMac
++    // {
++    //     public DESCFB8()
++    //     {
++    //         super(new CFBBlockCipherMac(new DESEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * DESede
++    //  */
++    // public static class DESedeCFB8
++    //     extends JCEMac
++    // {
++    //     public DESedeCFB8()
++    //     {
++    //         super(new CFBBlockCipherMac(new DESedeEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * SKIPJACK
++    //  */
++    // public static class SkipjackCFB8
++    //     extends JCEMac
++    // {
++    //     public SkipjackCFB8()
++    //     {
++    //         super(new CFBBlockCipherMac(new SkipjackEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * RC2CFB8
++    //  */
++    // public static class RC2CFB8
++    //     extends JCEMac
++    // {
++    //     public RC2CFB8()
++    //     {
++    //         super(new CFBBlockCipherMac(new RC2Engine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * RC5CFB8
++    //  */
++    // public static class RC5CFB8
++    //     extends JCEMac
++    // {
++    //     public RC5CFB8()
++    //     {
++    //         super(new CFBBlockCipherMac(new RC532Engine()));
++    //     }
++    // }
++    //
++    //
++    // /**
++    //  * DESede64
++    //  */
++    // public static class DESede64
++    //     extends JCEMac
++    // {
++    //     public DESede64()
++    //     {
++    //         super(new CBCBlockCipherMac(new DESedeEngine(), 64));
++    //     }
++    // }
++    //
++    // /**
++    //  * DESede64with7816-4Padding
++    //  */
++    // public static class DESede64with7816d4
++    //     extends JCEMac
++    // {
++    //     public DESede64with7816d4()
++    //     {
++    //         super(new CBCBlockCipherMac(new DESedeEngine(), 64, new ISO7816d4Padding()));
++    //     }
++    // }
++    //
++    // /**
++    //  * DES9797Alg3with7816-4Padding
++    //  */
++    // public static class DES9797Alg3with7816d4
++    //     extends JCEMac
++    // {
++    //     public DES9797Alg3with7816d4()
++    //     {
++    //         super(new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding()));
++    //     }
++    // }
++    //
++    // /**
++    //  * DES9797Alg3
++    //  */
++    // public static class DES9797Alg3
++    //     extends JCEMac
++    // {
++    //     public DES9797Alg3()
++    //     {
++    //         super(new ISO9797Alg3Mac(new DESEngine()));
++    //     }
++    // }
++    //
++    // /**
++    //  * MD2 HMac
++    //  */
++    // public static class MD2
++    //     extends JCEMac
++    // {
++    //     public MD2()
++    //     {
++    //         super(new HMac(new MD2Digest()));
++    //     }
++    // }
++    //
++    // /**
++    //  * MD4 HMac
++    //  */
++    // public static class MD4
++    //     extends JCEMac
++    // {
++    //     public MD4()
++    //     {
++    //         super(new HMac(new MD4Digest()));
++    //     }
++    // }
++    // END android-removed
+     
+     /**
+-     * DESede64
+-     */
+-    public static class DESede64
+-        extends JCEMac
+-    {
+-        public DESede64()
+-        {
+-            super(new CBCBlockCipherMac(new DESedeEngine(), 64));
+-        }
+-    }
+-
+-    /**
+-     * DESede64with7816-4Padding
+-     */
+-    public static class DESede64with7816d4
+-        extends JCEMac
+-    {
+-        public DESede64with7816d4()
+-        {
+-            super(new CBCBlockCipherMac(new DESedeEngine(), 64, new ISO7816d4Padding()));
+-        }
+-    }
+-
+-    /**
+-     * DES9797Alg3with7816-4Padding
+-     */
+-    public static class DES9797Alg3with7816d4
+-        extends JCEMac
+-    {
+-        public DES9797Alg3with7816d4()
+-        {
+-            super(new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding()));
+-        }
+-    }
+-
+-    /**
+-     * DES9797Alg3
+-     */
+-    public static class DES9797Alg3
+-        extends JCEMac
+-    {
+-        public DES9797Alg3()
+-        {
+-            super(new ISO9797Alg3Mac(new DESEngine()));
+-        }
+-    }
+-
+-    /**
+-     * MD2 HMac
+-     */
+-    public static class MD2
+-        extends JCEMac
+-    {
+-        public MD2()
+-        {
+-            super(new HMac(new MD2Digest()));
+-        }
+-    }
+-
+-    /**
+-     * MD4 HMac
+-     */
+-    public static class MD4
+-        extends JCEMac
+-    {
+-        public MD4()
+-        {
+-            super(new HMac(new MD4Digest()));
+-        }
+-    }
+-
+-    /**
+      * MD5 HMac
+      */
+     public static class MD5
+@@ -374,7 +390,7 @@
+             super(new HMac(new MD5Digest()));
+         }
+     }
+-
++    
+     /**
+      * SHA1 HMac
+      */
+@@ -386,18 +402,20 @@
+             super(new HMac(new SHA1Digest()));
+         }
+     }
+-
+-    /**
+-     * SHA-224 HMac
+-     */
+-    public static class SHA224
+-        extends JCEMac
+-    {
+-        public SHA224()
+-        {
+-            super(new HMac(new SHA224Digest()));
+-        }
+-    }
++    
++    // BEGIN android-removed
++    // /**
++    //  * SHA-224 HMac
++    //  */
++    // public static class SHA224
++    //     extends JCEMac
++    // {
++    //     public SHA224()
++    //     {
++    //         super(new HMac(new SHA224Digest()));
++    //     }
++    // }
++    // END android-removed
+     
+     /**
+      * SHA-256 HMac
+@@ -410,7 +428,7 @@
+             super(new HMac(new SHA256Digest()));
+         }
+     }
+-
++    
+     /**
+      * SHA-384 HMac
+      */
+@@ -422,15 +440,17 @@
+             super(new HMac(new SHA384Digest()));
+         }
+     }
+-
+-    public static class OldSHA384
+-        extends JCEMac
+-    {
+-        public OldSHA384()
+-        {
+-            super(new OldHMac(new SHA384Digest()));
+-        }
+-    }
++    
++    // BEGIN android-removed
++    // public static class OldSHA384
++    //     extends JCEMac
++    // {
++    //     public OldSHA384()
++    //     {
++    //         super(new OldHMac(new SHA384Digest()));
++    //     }
++    // }
++    // END android-removed
+     
+     /**
+      * SHA-512 HMac
+@@ -443,94 +463,96 @@
+             super(new HMac(new SHA512Digest()));
+         }
+     }
+-
+-    /**
+-     * SHA-512 HMac
+-     */
+-    public static class OldSHA512
+-        extends JCEMac
+-    {
+-        public OldSHA512()
+-        {
+-            super(new OldHMac(new SHA512Digest()));
+-        }
+-    }
+     
+-    /**
+-     * RIPEMD128 HMac
+-     */
+-    public static class RIPEMD128
+-        extends JCEMac
+-    {
+-        public RIPEMD128()
+-        {
+-            super(new HMac(new RIPEMD128Digest()));
+-        }
+-    }
+-
+-    /**
+-     * RIPEMD160 HMac
+-     */
+-    public static class RIPEMD160
+-        extends JCEMac
+-    {
+-        public RIPEMD160()
+-        {
+-            super(new HMac(new RIPEMD160Digest()));
+-        }
+-    }
+-
+-    /**
+-     * Tiger HMac
+-     */
+-    public static class Tiger
+-        extends JCEMac
+-    {
+-        public Tiger()
+-        {
+-            super(new HMac(new TigerDigest()));
+-        }
+-    }
+-
++    // BEGIN android-removed
++    // /**
++    //  * SHA-512 HMac
++    //  */
++    // public static class OldSHA512
++    //     extends JCEMac
++    // {
++    //     public OldSHA512()
++    //     {
++    //         super(new OldHMac(new SHA512Digest()));
++    //     }
++    // }
+     //
+-    // PKCS12 states that the same algorithm should be used
+-    // for the key generation as is used in the HMAC, so that
+-    // is what we do here.
++    // /**
++    //  * RIPEMD128 HMac
++    //  */
++    // public static class RIPEMD128
++    //     extends JCEMac
++    // {
++    //     public RIPEMD128()
++    //     {
++    //        super(new HMac(new RIPEMD128Digest()));
++    //     }
++    // }
+     //
+-
+-    /**
+-     * PBEWithHmacRIPEMD160
+-     */
+-    public static class PBEWithRIPEMD160
+-        extends JCEMac
+-    {
+-        public PBEWithRIPEMD160()
+-        {
+-            super(new HMac(new RIPEMD160Digest()), PKCS12, RIPEMD160, 160);
+-        }
+-    }
+-
+-    /**
+-     * PBEWithHmacSHA
+-     */
+-    public static class PBEWithSHA
+-        extends JCEMac
+-    {
+-        public PBEWithSHA()
+-        {
+-            super(new HMac(new SHA1Digest()), PKCS12, SHA1, 160);
+-        }
+-    }
+-
+-    /**
+-     * PBEWithHmacTiger
+-     */
+-    public static class PBEWithTiger
+-        extends JCEMac
+-    {
+-        public PBEWithTiger()
+-        {
+-            super(new HMac(new TigerDigest()), PKCS12, TIGER, 192);
+-        }
+-    }
++    // /**
++    //  * RIPEMD160 HMac
++    //  */
++    // public static class RIPEMD160
++    //     extends JCEMac
++    // {
++    //     public RIPEMD160()
++    //     {
++    //        super(new HMac(new RIPEMD160Digest()));
++    //     }
++    // }
++    //
++    // /**
++    //  * Tiger HMac
++    //  */
++    // public static class Tiger
++    //     extends JCEMac
++    // {
++    //     public Tiger()
++    //     {
++    //         super(new HMac(new TigerDigest()));
++    //     }
++    // }
++    //
++    // //
++    // // PKCS12 states that the same algorithm should be used
++    // // for the key generation as is used in the HMAC, so that
++    // // is what we do here.
++    // //
++    //
++    // /**
++    //  * PBEWithHmacRIPEMD160
++    //  */
++    // public static class PBEWithRIPEMD160
++    //     extends JCEMac
++    // {
++    //     public PBEWithRIPEMD160()
++    //     {
++    //         super(new HMac(new RIPEMD160Digest()), PKCS12, RIPEMD160, 160);
++    //     }
++    // }
++    //
++    //  /**
++    //   * PBEWithHmacSHA
++    //   */
++    //  public static class PBEWithSHA
++    //      extends JCEMac
++    //  {
++    //      public PBEWithSHA()
++    //      {
++    //          super(new HMac(new SHA1Digest()), PKCS12, SHA1, 160);
++    //      }
++    //  }
++    //
++    //  /**
++    //   * PBEWithHmacTiger
++    //   */
++    // public static class PBEWithTiger
++    //     extends JCEMac
++    // {
++    //     public PBEWithTiger()
++    //     {
++    //         super(new HMac(new TigerDigest()), PKCS12, TIGER, 192);
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSACipher.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSACipher.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSACipher.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSACipher.java	2010-06-25 23:17:26.000000000 +0000
+@@ -534,48 +534,50 @@
+         }
+     }
+ 
+-    static public class PKCS1v1_5Padding
+-        extends JCERSACipher
+-    {
+-        public PKCS1v1_5Padding()
+-        {
+-            super(new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class PKCS1v1_5Padding_PrivateOnly
+-        extends JCERSACipher
+-    {
+-        public PKCS1v1_5Padding_PrivateOnly()
+-        {
+-            super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class PKCS1v1_5Padding_PublicOnly
+-        extends JCERSACipher
+-    {
+-        public PKCS1v1_5Padding_PublicOnly()
+-        {
+-            super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class OAEPPadding
+-        extends JCERSACipher
+-    {
+-        public OAEPPadding()
+-        {
+-            super(OAEPParameterSpec.DEFAULT);
+-        }
+-    }
+-    
+-    static public class ISO9796d1Padding
+-        extends JCERSACipher
+-    {
+-        public ISO9796d1Padding()
+-        {
+-            super(new ISO9796d1Encoding(new RSABlindedEngine()));
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class PKCS1v1_5Padding
++    //     extends JCERSACipher
++    // {
++    //     public PKCS1v1_5Padding()
++    //     {
++    //         super(new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class PKCS1v1_5Padding_PrivateOnly
++    //     extends JCERSACipher
++    // {
++    //     public PKCS1v1_5Padding_PrivateOnly()
++    //     {
++    //         super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class PKCS1v1_5Padding_PublicOnly
++    //     extends JCERSACipher
++    // {
++    //     public PKCS1v1_5Padding_PublicOnly()
++    //     {
++    //         super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class OAEPPadding
++    //     extends JCERSACipher
++    // {
++    //     public OAEPPadding()
++    //     {
++    //         super(OAEPParameterSpec.DEFAULT);
++    //     }
++    // }
++    //
++    // static public class ISO9796d1Padding
++    //     extends JCERSACipher
++    // {
++    //     public ISO9796d1Padding()
++    //     {
++    //         super(new ISO9796d1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java	2010-06-25 23:17:26.000000000 +0000
+@@ -125,7 +125,9 @@
+      */
+     public byte[] getEncoded()
+     {
+-        PrivateKeyInfo          info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()), new RSAPrivateKeyStructure(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient()).getDERObject());
++        // BEGIN android-changed
++        PrivateKeyInfo          info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKeyStructure(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient()).getDERObject());
++        // END android-changed
+ 
+         return info.getDEREncoded();
+     }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPrivateKey.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPrivateKey.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPrivateKey.java	2010-06-25 23:17:26.000000000 +0000
+@@ -77,7 +77,9 @@
+ 
+     public byte[] getEncoded()
+     {
+-        PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()), new RSAPrivateKeyStructure(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO).getDERObject());
++        // BEGIN android-changed
++        PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKeyStructure(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO).getDERObject());
++        // END android-changed
+ 
+         return info.getDEREncoded();
+     }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPublicKey.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPublicKey.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCERSAPublicKey.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCERSAPublicKey.java	2010-06-25 23:17:26.000000000 +0000
+@@ -90,7 +90,9 @@
+ 
+     public byte[] getEncoded()
+     {
+-        SubjectPublicKeyInfo    info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()), new RSAPublicKeyStructure(getModulus(), getPublicExponent()).getDERObject());
++        // BEGIN android-changed
++        SubjectPublicKeyInfo    info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKeyStructure(getModulus(), getPublicExponent()).getDERObject());
++        // END android-changed
+ 
+         return info.getDEREncoded();
+     }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCESecretKeyFactory.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JCESecretKeyFactory.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JCESecretKeyFactory.java	2010-06-25 23:17:26.000000000 +0000
+@@ -321,29 +321,31 @@
+         }
+     }
+     
+-    /**
+-     * PBEWithMD2AndDES
+-     */
+-    static public class PBEWithMD2AndDES
+-        extends DESPBEKeyFactory
+-    {
+-        public PBEWithMD2AndDES()
+-        {
+-            super("PBEwithMD2andDES", PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, true, PKCS5S1, MD2, 64, 64);
+-        }
+-    }
+-
+-    /**
+-     * PBEWithMD2AndRC2
+-     */
+-    static public class PBEWithMD2AndRC2
+-        extends PBEKeyFactory
+-    {
+-        public PBEWithMD2AndRC2()
+-        {
+-            super("PBEwithMD2andRC2", PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, true, PKCS5S1, MD2, 64, 64);
+-        }
+-    }
++    // BEGIN android-removed
++    // /**
++    //  * PBEWithMD2AndDES
++    //  */
++    // static public class PBEWithMD2AndDES
++    //     extends DESPBEKeyFactory
++    // {
++    //     public PBEWithMD2AndDES()
++    //     {
++    //         super("PBEwithMD2andDES", PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, true, PKCS5S1, MD2, 64, 64);
++    //     }
++    // }
++    //
++    // /**
++    //  * PBEWithMD2AndRC2
++    //  */
++    // static public class PBEWithMD2AndRC2
++    //     extends PBEKeyFactory
++    // {
++    //     public PBEWithMD2AndRC2()
++    //     {
++    //         super("PBEwithMD2andRC2", PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, true, PKCS5S1, MD2, 64, 64);
++    //     }
++    // }
++    // END android-removed
+ 
+    /**
+     * PBEWithMD5AndDES
+@@ -357,41 +359,42 @@
+        }
+    }
+ 
+-   /**
+-    * PBEWithMD5AndRC2
+-    */
+-   static public class PBEWithMD5AndRC2
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithMD5AndRC2()
+-       {
+-           super("PBEwithMD5andRC2", PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, true, PKCS5S1, MD5, 64, 64);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHA1AndDES
+-    */
+-   static public class PBEWithSHA1AndDES
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA1AndDES()
+-       {
+-           super("PBEwithSHA1andDES", PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, true, PKCS5S1, SHA1, 64, 64);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHA1AndRC2
+-    */
+-   static public class PBEWithSHA1AndRC2
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA1AndRC2()
+-       {
+-           super("PBEwithSHA1andRC2", PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, true, PKCS5S1, SHA1, 64, 64);
+-       }
+-   }
++   // BEGIN android-removed
++   // /**
++   //  * PBEWithMD5AndRC2
++   //  */
++   // static public class PBEWithMD5AndRC2
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithMD5AndRC2()
++   //     {
++   //         super("PBEwithMD5andRC2", PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, true, PKCS5S1, MD5, 64, 64);
++   //     }
++   // }
++   // /**
++   //  * PBEWithSHA1AndDES
++   //  */
++   // static public class PBEWithSHA1AndDES
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA1AndDES()
++   //     {
++   //         super("PBEwithSHA1andDES", PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, true, PKCS5S1, SHA1, 64, 64);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA1AndRC2
++   //  */
++   // static public class PBEWithSHA1AndRC2
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA1AndRC2()
++   //     {
++   //         super("PBEwithSHA1andRC2", PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, true, PKCS5S1, SHA1, 64, 64);
++   //     }
++   // }
++   // END android-removed
+ 
+    /**
+     * PBEWithSHAAnd3-KeyTripleDES-CBC
+@@ -405,185 +408,186 @@
+        }
+    }
+ 
+-   /**
+-    * PBEWithSHAAnd2-KeyTripleDES-CBC
+-    */
+-   static public class PBEWithSHAAndDES2Key
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAndDES2Key()
+-       {
+-           super("PBEwithSHAandDES2Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, true, PKCS12, SHA1, 128, 64);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHAAnd128BitRC2-CBC
+-    */
+-   static public class PBEWithSHAAnd128BitRC2
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd128BitRC2()
+-       {
+-           super("PBEwithSHAand128BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, true, PKCS12, SHA1, 128, 64);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHAAnd40BitRC2-CBC
+-    */
+-   static public class PBEWithSHAAnd40BitRC2
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd40BitRC2()
+-       {
+-           super("PBEwithSHAand40BitRC2-CBC", PKCSObjectIdentifiers.pbewithSHAAnd40BitRC2_CBC, true, PKCS12, SHA1, 40, 64);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHAAndTwofish-CBC
+-    */
+-   static public class PBEWithSHAAndTwofish
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAndTwofish()
+-       {
+-           super("PBEwithSHAandTwofish-CBC", null, true, PKCS12, SHA1, 256, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHAAnd128BitRC4
+-    */
+-   static public class PBEWithSHAAnd128BitRC4
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd128BitRC4()
+-       {
+-           super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 128, 0);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithSHAAnd40BitRC4
+-    */
+-   static public class PBEWithSHAAnd40BitRC4
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd40BitRC4()
+-       {
+-           super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 40, 0);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithHmacRIPEMD160
+-    */
+-   public static class PBEWithRIPEMD160
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithRIPEMD160()
+-       {
+-           super("PBEwithHmacRIPEMD160", null, false, PKCS12, RIPEMD160, 160, 0);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithHmacSHA
+-    */
+-   public static class PBEWithSHA
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA()
+-       {
+-           super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
+-       }
+-   }
+-
+-   /**
+-    * PBEWithHmacTiger
+-    */
+-   public static class PBEWithTiger
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithTiger()
+-       {
+-           super("PBEwithHmacTiger", null, false, PKCS12, TIGER, 192, 0);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA1And128BitAES-BC
+-    */
+-   static public class PBEWithSHAAnd128BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd128BitAESBC()
+-       {
+-           super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA1And192BitAES-BC
+-    */
+-   static public class PBEWithSHAAnd192BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd192BitAESBC()
+-       {
+-           super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA1And256BitAES-BC
+-    */
+-   static public class PBEWithSHAAnd256BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHAAnd256BitAESBC()
+-       {
+-           super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA256And128BitAES-BC
+-    */
+-   static public class PBEWithSHA256And128BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA256And128BitAESBC()
+-       {
+-           super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA256And192BitAES-BC
+-    */
+-   static public class PBEWithSHA256And192BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA256And192BitAESBC()
+-       {
+-           super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
+-       }
+-   }
+-   
+-   /**
+-    * PBEWithSHA256And256BitAES-BC
+-    */
+-   static public class PBEWithSHA256And256BitAESBC
+-       extends PBEKeyFactory
+-   {
+-       public PBEWithSHA256And256BitAESBC()
+-       {
+-           super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
+-       }
+-   }
++   // BEGIN android-removed
++   // /**
++   //  * PBEWithSHAAnd2-KeyTripleDES-CBC
++   //  */
++   // static public class PBEWithSHAAndDES2Key
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAndDES2Key()
++   //     {
++   //         super("PBEwithSHAandDES2Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, true, PKCS12, SHA1, 128, 64);
++   //     }
++   // }
++   // /**
++   //  * PBEWithSHAAnd128BitRC2-CBC
++   //  */
++   // static public class PBEWithSHAAnd128BitRC2
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd128BitRC2()
++   //     {
++   //         super("PBEwithSHAand128BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, true, PKCS12, SHA1, 128, 64);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHAAnd40BitRC2-CBC
++   //  */
++   // static public class PBEWithSHAAnd40BitRC2
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd40BitRC2()
++   //     {
++   //         super("PBEwithSHAand40BitRC2-CBC", PKCSObjectIdentifiers.pbewithSHAAnd40BitRC2_CBC, true, PKCS12, SHA1, 40, 64);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHAAndTwofish-CBC
++   //  */
++   // static public class PBEWithSHAAndTwofish
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAndTwofish()
++   //     {
++   //         super("PBEwithSHAandTwofish-CBC", null, true, PKCS12, SHA1, 256, 128);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHAAnd128BitRC4
++   //  */
++   // static public class PBEWithSHAAnd128BitRC4
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd128BitRC4()
++   //     {
++   //         super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 128, 0);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHAAnd40BitRC4
++   //  */
++   // static public class PBEWithSHAAnd40BitRC4
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd40BitRC4()
++   //     {
++   //         super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 40, 0);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithHmacRIPEMD160
++   //  */
++   // public static class PBEWithRIPEMD160
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithRIPEMD160()
++   //     {
++   //         super("PBEwithHmacRIPEMD160", null, false, PKCS12, RIPEMD160, 160, 0);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithHmacSHA
++   //  */
++   // public static class PBEWithSHA
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA()
++   //     {
++   //         super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithHmacTiger
++   //  */
++   // public static class PBEWithTiger
++   //    extends PBEKeyFactory
++   // {
++   //     public PBEWithTiger()
++   //     {
++   //         super("PBEwithHmacTiger", null, false, PKCS12, TIGER, 192, 0);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA1And128BitAES-BC
++   //  */
++   // static public class PBEWithSHAAnd128BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd128BitAESBC()
++   //     {
++   //         super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA1And192BitAES-BC
++   //  */
++   // static public class PBEWithSHAAnd192BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd192BitAESBC()
++   //     {
++   //         super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA1And256BitAES-BC
++   //  */
++   // static public class PBEWithSHAAnd256BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHAAnd256BitAESBC()
++   //     {
++   //         super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA256And128BitAES-BC
++   //  */
++   // static public class PBEWithSHA256And128BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA256And128BitAESBC()
++   //     {
++   //         super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
++   //     }
++   // }
++   //   
++   // /**
++   //  * PBEWithSHA256And192BitAES-BC
++   //  */
++   // static public class PBEWithSHA256And192BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA256And192BitAESBC()
++   //     {
++   //         super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
++   //     }
++   // }
++   //
++   // /**
++   //  * PBEWithSHA256And256BitAES-BC
++   //  */
++   // static public class PBEWithSHA256And256BitAESBC
++   //     extends PBEKeyFactory
++   // {
++   //     public PBEWithSHA256And256BitAESBC()
++   //     {
++   //         super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
++   //     }
++   // }
++   // END android-removed
+    
+    /**
+     * PBEWithMD5And128BitAES-OpenSSL
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java	2010-06-25 23:17:26.000000000 +0000
+@@ -2,14 +2,18 @@
+ 
+ import org.bouncycastle.crypto.generators.DHParametersGenerator;
+ import org.bouncycastle.crypto.generators.DSAParametersGenerator;
+-import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
+-import org.bouncycastle.crypto.generators.GOST3410ParametersGenerator;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
++// import org.bouncycastle.crypto.generators.GOST3410ParametersGenerator;
++// END android-removed
+ import org.bouncycastle.crypto.params.DHParameters;
+ import org.bouncycastle.crypto.params.DSAParameters;
+-import org.bouncycastle.crypto.params.ElGamalParameters;
+-import org.bouncycastle.crypto.params.GOST3410Parameters;
+-import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+-import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.params.ElGamalParameters;
++// import org.bouncycastle.crypto.params.GOST3410Parameters;
++// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
++// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
++// END android-removed
+ 
+ import javax.crypto.spec.DHGenParameterSpec;
+ import javax.crypto.spec.DHParameterSpec;
+@@ -144,196 +148,198 @@
+         }
+     }
+ 
+-    public static class GOST3410
+-        extends JDKAlgorithmParameterGenerator
+-    {
+-        protected void engineInit(
+-                AlgorithmParameterSpec  genParamSpec,
+-                SecureRandom            random)
+-        throws InvalidAlgorithmParameterException
+-        {
+-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST3410 parameter generation.");
+-        }
+-        
+-        protected AlgorithmParameters engineGenerateParameters()
+-        {
+-            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+-            
+-            if (random != null)
+-            {
+-                pGen.init(strength, 2, random);
+-            }
+-            else
+-            {
+-                pGen.init(strength, 2, new SecureRandom());
+-            }
+-            
+-            GOST3410Parameters p = pGen.generateParameters();
+-            
+-            AlgorithmParameters params;
+-            
+-            try
+-            {
+-                params = AlgorithmParameters.getInstance("GOST3410", "BC");
+-                params.init(new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(p.getP(), p.getQ(), p.getA())));
+-            }
+-            catch (Exception e)
+-            {
+-                throw new RuntimeException(e.getMessage());
+-            }
+-            
+-            return params;
+-        }
+-    }
+-    
+-    public static class ElGamal
+-        extends JDKAlgorithmParameterGenerator
+-    {
+-        private int l = 0;
+-        
+-        protected void engineInit(
+-            AlgorithmParameterSpec  genParamSpec,
+-            SecureRandom            random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            if (!(genParamSpec instanceof DHGenParameterSpec))
+-            {
+-                throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation");
+-            }
+-            DHGenParameterSpec  spec = (DHGenParameterSpec)genParamSpec;
+-
+-            this.strength = spec.getPrimeSize();
+-            this.l = spec.getExponentSize();
+-            this.random = random;
+-        }
+-
+-        protected AlgorithmParameters engineGenerateParameters()
+-        {
+-            ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
+-
+-            if (random != null)
+-            {
+-                pGen.init(strength, 20, random);
+-            }
+-            else
+-            {
+-                pGen.init(strength, 20, new SecureRandom());
+-            }
+-
+-            ElGamalParameters p = pGen.generateParameters();
+-
+-            AlgorithmParameters params;
+-
+-            try
+-            {
+-                params = AlgorithmParameters.getInstance("ElGamal", "BC");
+-                params.init(new DHParameterSpec(p.getP(), p.getG(), l));
+-            }
+-            catch (Exception e)
+-            {
+-                throw new RuntimeException(e.getMessage());
+-            }
+-
+-            return params;
+-        }
+-    }
+-
+-    public static class DES
+-        extends JDKAlgorithmParameterGenerator
+-    {
+-        protected void engineInit(
+-            AlgorithmParameterSpec  genParamSpec,
+-            SecureRandom            random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DES parameter generation.");
+-        }
+-
+-        protected AlgorithmParameters engineGenerateParameters()
+-        {
+-            byte[]  iv = new byte[8];
+-
+-            if (random == null)
+-            {
+-                random = new SecureRandom();
+-            }
+-
+-            random.nextBytes(iv);
+-
+-            AlgorithmParameters params;
+-
+-            try
+-            {
+-                params = AlgorithmParameters.getInstance("DES", "BC");
+-                params.init(new IvParameterSpec(iv));
+-            }
+-            catch (Exception e)
+-            {
+-                throw new RuntimeException(e.getMessage());
+-            }
+-
+-            return params;
+-        }
+-    }
+-
+-    public static class RC2
+-        extends JDKAlgorithmParameterGenerator
+-    {
+-        RC2ParameterSpec    spec = null;
+-
+-        protected void engineInit(
+-            AlgorithmParameterSpec  genParamSpec,
+-            SecureRandom            random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            if (genParamSpec instanceof RC2ParameterSpec)
+-            {
+-                spec = (RC2ParameterSpec)genParamSpec;
+-                return;
+-            }
+-
+-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for RC2 parameter generation.");
+-        }
+-
+-        protected AlgorithmParameters engineGenerateParameters()
+-        {
+-            AlgorithmParameters params;
+-
+-            if (spec == null)
+-            {
+-                byte[]  iv = new byte[8];
+-
+-                if (random == null)
+-                {
+-                    random = new SecureRandom();
+-                }
+-
+-                random.nextBytes(iv);
+-
+-                try
+-                {
+-                    params = AlgorithmParameters.getInstance("RC2", "BC");
+-                    params.init(new IvParameterSpec(iv));
+-                }
+-                catch (Exception e)
+-                {
+-                    throw new RuntimeException(e.getMessage());
+-                }
+-            }
+-            else
+-            {
+-                try
+-                {
+-                    params = AlgorithmParameters.getInstance("RC2", "BC");
+-                    params.init(spec);
+-                }
+-                catch (Exception e)
+-                {
+-                    throw new RuntimeException(e.getMessage());
+-                }
+-            }
+-
+-            return params;
+-        }
+-    }
++   // BEGIN android-removed
++   // public static class GOST3410
++   //     extends JDKAlgorithmParameterGenerator
++   // {
++   //     protected void engineInit(
++   //             AlgorithmParameterSpec  genParamSpec,
++   //             SecureRandom            random)
++   //     throws InvalidAlgorithmParameterException
++   //     {
++   //         throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST3410 parameter generation.");
++   //     }
++   //       
++   //     protected AlgorithmParameters engineGenerateParameters()
++   //     {
++   //         GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
++   //           
++   //         if (random != null)
++   //         {
++   //             pGen.init(strength, 2, random);
++   //         }
++   //         else
++   //         {
++   //             pGen.init(strength, 2, new SecureRandom());
++   //         }
++   //           
++   //         GOST3410Parameters p = pGen.generateParameters();
++   //           
++   //         AlgorithmParameters params;
++   //           
++   //         try
++   //         {
++   //             params = AlgorithmParameters.getInstance("GOST3410", "BC");
++   //             params.init(new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(p.getP(), p.getQ(), p.getA())));
++   //         }
++   //         catch (Exception e)
++   //         {
++   //             throw new RuntimeException(e.getMessage());
++   //         }
++   //           
++   //         return params;
++   //     }
++   // }
++   //   
++   // public static class ElGamal
++   //     extends JDKAlgorithmParameterGenerator
++   // {
++   //     private int l = 0;
++   //       
++   //     protected void engineInit(
++   //         AlgorithmParameterSpec  genParamSpec,
++   //         SecureRandom            random)
++   //         throws InvalidAlgorithmParameterException
++   //     {
++   //         if (!(genParamSpec instanceof DHGenParameterSpec))
++   //         {
++   //             throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation");
++   //         }
++   //         DHGenParameterSpec  spec = (DHGenParameterSpec)genParamSpec;
++   //
++   //         this.strength = spec.getPrimeSize();
++   //         this.l = spec.getExponentSize();
++   //         this.random = random;
++   //     }
++   //
++   //     protected AlgorithmParameters engineGenerateParameters()
++   //     {
++   //         ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
++   //
++   //         if (random != null)
++   //         {
++   //             pGen.init(strength, 20, random);
++   //         }
++   //         else
++   //         {
++   //             pGen.init(strength, 20, new SecureRandom());
++   //         }
++   //
++   //         ElGamalParameters p = pGen.generateParameters();
++   //
++   //         AlgorithmParameters params;
++   //
++   //         try
++   //         {
++   //             params = AlgorithmParameters.getInstance("ElGamal", "BC");
++   //             params.init(new DHParameterSpec(p.getP(), p.getG(), l));
++   //         }
++   //         catch (Exception e)
++   //         {
++   //             throw new RuntimeException(e.getMessage());
++   //         }
++   //
++   //         return params;
++   //     }
++   // }
++   //
++   //  public static class DES
++   //      extends JDKAlgorithmParameterGenerator
++   //  {
++   //      protected void engineInit(
++   //          AlgorithmParameterSpec  genParamSpec,
++   //          SecureRandom            random)
++   //          throws InvalidAlgorithmParameterException
++   //      {
++   //          throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DES parameter generation.");
++   //      }
++   //
++   //      protected AlgorithmParameters engineGenerateParameters()
++   //      {
++   //          byte[]  iv = new byte[8];
++   //
++   //          if (random == null)
++   //          {
++   //              random = new SecureRandom();
++   //          }
++   //
++   //          random.nextBytes(iv);
++   //
++   //          AlgorithmParameters params;
++   //
++   //          try
++   //          {
++   //              params = AlgorithmParameters.getInstance("DES", "BC");
++   //              params.init(new IvParameterSpec(iv));
++   //          }
++   //          catch (Exception e)
++   //          {
++   //              throw new RuntimeException(e.getMessage());
++   //          }
++   //
++   //          return params;
++   //      }
++   //  }
++   //
++   //  public static class RC2
++   //      extends JDKAlgorithmParameterGenerator
++   //  {
++   //      RC2ParameterSpec    spec = null;
++   //
++   //      protected void engineInit(
++   //          AlgorithmParameterSpec  genParamSpec,
++   //          SecureRandom            random)
++   //          throws InvalidAlgorithmParameterException
++   //      {
++   //          if (genParamSpec instanceof RC2ParameterSpec)
++   //          {
++   //              spec = (RC2ParameterSpec)genParamSpec;
++   //              return;
++   //          }
++   //
++   //          throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for RC2 parameter generation.");
++   //      }
++   //
++   //      protected AlgorithmParameters engineGenerateParameters()
++   //      {
++   //          AlgorithmParameters params;
++   //
++   //          if (spec == null)
++   //          {
++   //              byte[]  iv = new byte[8];
++   //
++   //              if (random == null)
++   //              {
++   //                  random = new SecureRandom();
++   //              }
++   //
++   //              random.nextBytes(iv);
++   //
++   //              try
++   //              {
++   //                  params = AlgorithmParameters.getInstance("RC2", "BC");
++   //                  params.init(new IvParameterSpec(iv));
++   //              }
++   //              catch (Exception e)
++   //              {
++   //                  throw new RuntimeException(e.getMessage());
++   //              }
++   //          }
++   //          else
++   //          {
++   //              try
++   //              {
++   //                  params = AlgorithmParameters.getInstance("RC2", "BC");
++   //                  params.init(spec);
++   //              }
++   //              catch (Exception e)
++   //              {
++   //                  throw new RuntimeException(e.getMessage());
++   //              }
++   //          }
++   //
++   //          return params;
++   //      }
++   //  }
++   // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java	2010-06-25 23:17:26.000000000 +0000
+@@ -10,21 +10,29 @@
+ import org.bouncycastle.asn1.DERObjectIdentifier;
+ import org.bouncycastle.asn1.DEROctetString;
+ import org.bouncycastle.asn1.DERSequence;
+-import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+-import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
++// import org.bouncycastle.asn1.oiw.ElGamalParameter;
++// END android-removed
+ import org.bouncycastle.asn1.pkcs.DHParameter;
+ import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
+ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+-import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
++// END android-removed
+ import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
+ import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+-import org.bouncycastle.asn1.pkcs.PBKDF2Params;
++// BEGIN android-removed
++// import org.bouncycastle.asn1.pkcs.PBKDF2Params;
++// END android-removed
+ import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+ import org.bouncycastle.asn1.x509.DSAParameter;
+-import org.bouncycastle.jce.spec.ElGamalParameterSpec;
+-import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+-import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
+-import org.bouncycastle.jce.spec.IESParameterSpec;
++// BEGIN android-removed
++// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
++// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
++// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
++// import org.bouncycastle.jce.spec.IESParameterSpec;
++// END android-removed
+ import org.bouncycastle.util.Arrays;
+ 
+ import javax.crypto.spec.DHParameterSpec;
+@@ -32,7 +40,9 @@
+ import javax.crypto.spec.OAEPParameterSpec;
+ import javax.crypto.spec.PBEParameterSpec;
+ import javax.crypto.spec.PSource;
+-import javax.crypto.spec.RC2ParameterSpec;
++// BEGIN android-removed
++// import javax.crypto.spec.RC2ParameterSpec;
++// END android-removed
+ import java.io.IOException;
+ import java.security.AlgorithmParametersSpi;
+ import java.security.spec.AlgorithmParameterSpec;
+@@ -68,13 +78,13 @@
+         extends JDKAlgorithmParameters
+     {
+         private byte[]  iv;
+-
++    
+         protected byte[] engineGetEncoded() 
+             throws IOException
+         {
+             return engineGetEncoded("ASN.1");
+         }
+-
++    
+         protected byte[] engineGetEncoded(
+             String format) 
+             throws IOException
+@@ -83,15 +93,15 @@
+             {
+                  return new DEROctetString(engineGetEncoded("RAW")).getEncoded();
+             }
+-            
++    
+             if (format.equals("RAW"))
+             {
+                 return Arrays.clone(iv);
+             }
+-
++    
+             return null;
+         }
+-
++    
+         protected AlgorithmParameterSpec localEngineGetParameterSpec(
+             Class paramSpec) 
+             throws InvalidParameterSpecException
+@@ -100,10 +110,10 @@
+             {
+                 return new IvParameterSpec(iv);
+             }
+-
++    
+             throw new InvalidParameterSpecException("unknown parameter spec passed to IV parameters object.");
+         }
+-
++    
+         protected void engineInit(
+             AlgorithmParameterSpec paramSpec) 
+             throws InvalidParameterSpecException
+@@ -112,10 +122,10 @@
+             {
+                 throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object");
+             }
+-
++    
+             this.iv = ((IvParameterSpec)paramSpec).getIV();
+         }
+-
++    
+         protected void engineInit(
+             byte[] params) 
+             throws IOException
+@@ -127,13 +137,13 @@
+                     && params[0] == 0x04 && params[1] == params.length - 2)
+             {
+                 ASN1OctetString oct = (ASN1OctetString)ASN1Object.fromByteArray(params);
+-
++    
+                 params = oct.getOctets();
+             }
+-
++    
+             this.iv = Arrays.clone(params);
+         }
+-
++    
+         protected void engineInit(
+             byte[] params,
+             String format) 
+@@ -144,292 +154,294 @@
+                 try
+                 {
+                     ASN1OctetString oct = (ASN1OctetString)ASN1Object.fromByteArray(params);
+-
++    
+                     engineInit(oct.getOctets());
+                 }
+                 catch (Exception e)
+                 {
+                     throw new IOException("Exception decoding: " + e);
+                 }
+-                
++    
+                 return;
+             }
+-
++    
+             if (format.equals("RAW"))
+             {
+                 engineInit(params);
+                 return;
+             }
+-
++    
+             throw new IOException("Unknown parameters format in IV parameters object");
+         }
+-
++    
+         protected String engineToString() 
+         {
+             return "IV Parameters";
+         }
+     }
+-
+-    public static class RC2AlgorithmParameters
+-        extends JDKAlgorithmParameters
+-    {
+-        private static final short[] table = {
+-           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[] ekb = {
+-           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
+-        };
+-
+-        private byte[]  iv;
+-        private int     parameterVersion = 58;
+-
+-        protected byte[] engineGetEncoded() 
+-        {
+-            return Arrays.clone(iv);
+-        }
+-
+-        protected byte[] engineGetEncoded(
+-            String format) 
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format))
+-            {
+-                if (parameterVersion == -1)
+-                {
+-                    return new RC2CBCParameter(engineGetEncoded()).getEncoded();
+-                }
+-                else
+-                {
+-                    return new RC2CBCParameter(parameterVersion, engineGetEncoded()).getEncoded();
+-                }
+-            }
+-
+-            if (format.equals("RAW"))
+-            {
+-                return engineGetEncoded();
+-            }
+-
+-            return null;
+-        }
+-
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == RC2ParameterSpec.class)
+-            {
+-                if (parameterVersion != -1)
+-                {
+-                    if (parameterVersion < 256)
+-                    {
+-                        return new RC2ParameterSpec(ekb[parameterVersion], iv);
+-                    }
+-                    else
+-                    {
+-                        return new RC2ParameterSpec(parameterVersion, iv);
+-                    }
+-                }
+-            }
+-
+-            if (paramSpec == IvParameterSpec.class)
+-            {
+-                return new IvParameterSpec(iv);
+-            }
+-
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to RC2 parameters object.");
+-        }
+-
+-        protected void engineInit(
+-            AlgorithmParameterSpec paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec instanceof IvParameterSpec)
+-            {
+-                this.iv = ((IvParameterSpec)paramSpec).getIV();
+-            }
+-            else if (paramSpec instanceof RC2ParameterSpec)
+-            {
+-                int effKeyBits = ((RC2ParameterSpec)paramSpec).getEffectiveKeyBits();
+-                if (effKeyBits != -1)
+-                {
+-                    if (effKeyBits < 256)
+-                    {
+-                        parameterVersion = table[effKeyBits];
+-                    }
+-                    else
+-                    {
+-                        parameterVersion = effKeyBits;
+-                    }
+-                }
+-
+-                this.iv = ((RC2ParameterSpec)paramSpec).getIV();
+-            }
+-            else
+-            {
+-                throw new InvalidParameterSpecException("IvParameterSpec or RC2ParameterSpec required to initialise a RC2 parameters algorithm parameters object");
+-            }
+-        }
+-
+-        protected void engineInit(
+-            byte[] params) 
+-            throws IOException
+-        {
+-            this.iv = Arrays.clone(params);
+-        }
+-
+-        protected void engineInit(
+-            byte[] params,
+-            String format) 
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format))
+-            {
+-                RC2CBCParameter p = RC2CBCParameter.getInstance(ASN1Object.fromByteArray(params));
+-
+-                if (p.getRC2ParameterVersion() != null)
+-                {
+-                    parameterVersion = p.getRC2ParameterVersion().intValue();
+-                }
+-
+-                iv = p.getIV();
+-
+-                return;
+-            }
+-
+-            if (format.equals("RAW"))
+-            {
+-                engineInit(params);
+-                return;
+-            }
+-
+-            throw new IOException("Unknown parameters format in IV parameters object");
+-        }
+-
+-        protected String engineToString() 
+-        {
+-            return "RC2 Parameters";
+-        }
+-    }
+-
+-    public static class PBKDF2
+-        extends JDKAlgorithmParameters
+-    {
+-        PBKDF2Params params;
+-
+-        protected byte[] engineGetEncoded()
+-        {
+-            try
+-            {
+-                return params.getEncoded(ASN1Encodable.DER);
+-            }
+-            catch (IOException e)
+-            {
+-                throw new RuntimeException("Oooops! " + e.toString());
+-            }
+-        }
+-
+-        protected byte[] engineGetEncoded(
+-            String format)
+-        {
+-            if (isASN1FormatString(format))
+-            {
+-                return engineGetEncoded();
+-            }
+-
+-            return null;
+-        }
+-
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec)
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == PBEParameterSpec.class)
+-            {
+-                return new PBEParameterSpec(params.getSalt(),
+-                                params.getIterationCount().intValue());
+-            }
+-
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
+-        }
+-
+-        protected void engineInit(
+-            AlgorithmParameterSpec paramSpec)
+-            throws InvalidParameterSpecException
+-        {
+-            if (!(paramSpec instanceof PBEParameterSpec))
+-            {
+-                throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
+-            }
+-
+-            PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
+-
+-            this.params = new PBKDF2Params(pbeSpec.getSalt(),
+-                                pbeSpec.getIterationCount());
+-        }
+-
+-        protected void engineInit(
+-            byte[] params)
+-            throws IOException
+-        {
+-            this.params = PBKDF2Params.getInstance(ASN1Object.fromByteArray(params));
+-        }
+-
+-        protected void engineInit(
+-            byte[] params,
+-            String format)
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format))
+-            {
+-                engineInit(params);
+-                return;
+-            }
+-
+-            throw new IOException("Unknown parameters format in PWRIKEK parameters object");
+-        }
+-
+-        protected String engineToString()
+-        {
+-            return "PBKDF2 Parameters";
+-        }
+-    }
+-
++    
++    // BEGIN android-removed
++    // public static class RC2AlgorithmParameters
++    //     extends JDKAlgorithmParameters
++    // {
++    //     private static final short[] table = {
++    //        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[] ekb = {
++    //        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
++    //     };
++    //
++    //     private byte[]  iv;
++    //     private int     parameterVersion = 58;
++    //
++    //     protected byte[] engineGetEncoded() 
++    //     {
++    //         return Arrays.clone(iv);
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format))
++    //         {
++    //             if (parameterVersion == -1)
++    //             {
++    //                 return new RC2CBCParameter(engineGetEncoded()).getEncoded();
++    //             }
++    //             else
++    //             {
++    //                 return new RC2CBCParameter(parameterVersion, engineGetEncoded()).getEncoded();
++    //             }
++    //         }
++    //
++    //         if (format.equals("RAW"))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //         Class paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == RC2ParameterSpec.class)
++    //         {
++    //             if (parameterVersion != -1)
++    //             {
++    //                 if (parameterVersion < 256)
++    //                 {
++    //                     return new RC2ParameterSpec(ekb[parameterVersion], iv);
++    //                 }
++    //                 else
++    //                 {
++    //                     return new RC2ParameterSpec(parameterVersion, iv);
++    //                 }
++    //             }
++    //         }
++    //
++    //         if (paramSpec == IvParameterSpec.class)
++    //         {
++    //             return new IvParameterSpec(iv);
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to RC2 parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec instanceof IvParameterSpec)
++    //         {
++    //             this.iv = ((IvParameterSpec)paramSpec).getIV();
++    //         }
++    //         else if (paramSpec instanceof RC2ParameterSpec)
++    //         {
++    //             int effKeyBits = ((RC2ParameterSpec)paramSpec).getEffectiveKeyBits();
++    //             if (effKeyBits != -1)
++    //             {
++    //                 if (effKeyBits < 256)
++    //                 {
++    //                     parameterVersion = table[effKeyBits];
++    //                 }
++    //                 else
++    //                 {
++    //                     parameterVersion = effKeyBits;
++    //                 }
++    //             }
++    //
++    //             this.iv = ((RC2ParameterSpec)paramSpec).getIV();
++    //         }
++    //         else
++    //         {
++    //             throw new InvalidParameterSpecException("IvParameterSpec or RC2ParameterSpec required to initialise a RC2 parameters algorithm parameters object");
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params) 
++    //         throws IOException
++    //     {
++    //         this.iv = Arrays.clone(params);
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params,
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format))
++    //         {
++    //             RC2CBCParameter p = RC2CBCParameter.getInstance(ASN1Object.fromByteArray(params));
++    //
++    //             if (p.getRC2ParameterVersion() != null)
++    //             {
++    //                 parameterVersion = p.getRC2ParameterVersion().intValue();
++    //             }
++    //
++    //             iv = p.getIV();
++    //
++    //             return;
++    //         }
++    //
++    //         if (format.equals("RAW"))
++    //         {
++    //             engineInit(params);
++    //             return;
++    //         }
++    //
++    //         throw new IOException("Unknown parameters format in IV parameters object");
++    //     }
++    //
++    //     protected String engineToString() 
++    //     {
++    //         return "RC2 Parameters";
++    //     }
++    // }
++    //
++    // public static class PBKDF2
++    //     extends JDKAlgorithmParameters
++    // {
++    //     PBKDF2Params params;
++    //
++    //     protected byte[] engineGetEncoded()
++    //     {
++    //         try
++    //         {
++    //             return params.getEncoded(ASN1Encodable.DER);
++    //         }
++    //         catch (IOException e)
++    //         {
++    //             throw new RuntimeException("Oooops! " + e.toString());
++    //         }
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //         String format)
++    //     {
++    //         if (isASN1FormatString(format))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //         Class paramSpec)
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == PBEParameterSpec.class)
++    //         {
++    //             return new PBEParameterSpec(params.getSalt(),
++    //                             params.getIterationCount().intValue());
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec paramSpec)
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (!(paramSpec instanceof PBEParameterSpec))
++    //         {
++    //             throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
++    //         }
++    //
++    //         PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
++    //
++    //         this.params = new PBKDF2Params(pbeSpec.getSalt(),
++    //                             pbeSpec.getIterationCount());
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params)
++    //         throws IOException
++    //     {
++    //         this.params = PBKDF2Params.getInstance(ASN1Object.fromByteArray(params));
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params,
++    //         String format)
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format))
++    //         {
++    //             engineInit(params);
++    //             return;
++    //         }
++    //
++    //         throw new IOException("Unknown parameters format in PWRIKEK parameters object");
++    //     }
++    //
++    //     protected String engineToString()
++    //     {
++    //         return "PBKDF2 Parameters";
++    //     }
++    // }
++    // END android-removed
++    
+     public static class PKCS12PBE
+         extends JDKAlgorithmParameters
+     {
+         PKCS12PBEParams params;
+-
++    
+         protected byte[] engineGetEncoded() 
+         {
+             try
+@@ -441,7 +453,7 @@
+                 throw new RuntimeException("Oooops! " + e.toString());
+             }
+         }
+-
++    
+         protected byte[] engineGetEncoded(
+             String format) 
+         {
+@@ -449,10 +461,10 @@
+             {
+                 return engineGetEncoded();
+             }
+-
++    
+             return null;
+         }
+-
++    
+         protected AlgorithmParameterSpec localEngineGetParameterSpec(
+             Class paramSpec) 
+             throws InvalidParameterSpecException
+@@ -462,10 +474,10 @@
+                 return new PBEParameterSpec(params.getIV(),
+                                 params.getIterations().intValue());
+             }
+-
++    
+             throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
+         }
+-
++    
+         protected void engineInit(
+             AlgorithmParameterSpec paramSpec) 
+             throws InvalidParameterSpecException
+@@ -474,20 +486,20 @@
+             {
+                 throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
+             }
+-
++    
+             PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
+-
++    
+             this.params = new PKCS12PBEParams(pbeSpec.getSalt(),
+                                 pbeSpec.getIterationCount());
+         }
+-
++    
+         protected void engineInit(
+             byte[] params) 
+             throws IOException
+         {
+             this.params = PKCS12PBEParams.getInstance(ASN1Object.fromByteArray(params));
+         }
+-
++    
+         protected void engineInit(
+             byte[] params,
+             String format) 
+@@ -498,10 +510,10 @@
+                 engineInit(params);
+                 return;
+             }
+-
++    
+             throw new IOException("Unknown parameters format in PKCS12 PBE parameters object");
+         }
+-
++    
+         protected String engineToString() 
+         {
+             return "PKCS12 PBE Parameters";
+@@ -725,381 +737,387 @@
+         }
+     }
+     
+-    public static class GOST3410
++    // BEGIN android-removed
++    // public static class GOST3410
++    //     extends JDKAlgorithmParameters
++    // {
++    //     GOST3410ParameterSpec     currentSpec;
++    //
++    //     /**
++    //      * Return the X.509 ASN.1 structure GOST3410Parameter.
++    //      * <p>
++    //      * <pre>
++    //      *  GOST3410Parameter ::= SEQUENCE {
++    //      *                   prime INTEGER, -- p
++    //      *                   subprime INTEGER, -- q
++    //      *                   base INTEGER, -- a}
++    //      * </pre>
++    //      */
++    //     protected byte[] engineGetEncoded()
++    //     {
++    //         GOST3410PublicKeyAlgParameters gost3410P = new GOST3410PublicKeyAlgParameters(new DERObjectIdentifier(currentSpec.getPublicKeyParamSetOID()), new DERObjectIdentifier(currentSpec.getDigestParamSetOID()), new DERObjectIdentifier(currentSpec.getEncryptionParamSetOID()));
++    //
++    //         try
++    //         {
++    //             return gost3410P.getEncoded(ASN1Encodable.DER);
++    //         }
++    //         catch (IOException e)
++    //         {
++    //             throw new RuntimeException("Error encoding GOST3410Parameters");
++    //         }
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //             String format)
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //             Class paramSpec)
++    //     throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == GOST3410PublicKeyParameterSetSpec.class)
++    //         {
++    //             return currentSpec;
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to GOST3410 parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //             AlgorithmParameterSpec paramSpec)
++    //     throws InvalidParameterSpecException
++    //     {
++    //         if (!(paramSpec instanceof GOST3410ParameterSpec))
++    //         {
++    //             throw new InvalidParameterSpecException("GOST3410ParameterSpec required to initialise a GOST3410 algorithm parameters object");
++    //         }
++    //
++    //         this.currentSpec = (GOST3410ParameterSpec)paramSpec;
++    //     }
++    //
++    //     protected void engineInit(
++    //             byte[] params)
++    //     throws IOException
++    //     {
++    //         try
++    //         {
++    //             ASN1Sequence seq = (ASN1Sequence) ASN1Object.fromByteArray(params);
++    //
++    //             this.currentSpec = GOST3410ParameterSpec.fromPublicKeyAlg(
++    //                 new GOST3410PublicKeyAlgParameters(seq));
++    //         }
++    //         catch (ClassCastException e)
++    //         {
++    //             throw new IOException("Not a valid GOST3410 Parameter encoding.");
++    //         }
++    //         catch (ArrayIndexOutOfBoundsException e)
++    //         {
++    //             throw new IOException("Not a valid GOST3410 Parameter encoding.");
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //             byte[] params,
++    //             String format)
++    //     throws IOException
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             engineInit(params);
++    //         }
++    //         else
++    //         {
++    //             throw new IOException("Unknown parameter format " + format);
++    //         }
++    //     }
++    //
++    //     protected String engineToString()
++    //     {
++    //         return "GOST3410 Parameters";
++    //     }
++    // }
++
++    // public static class ElGamal
++    //     extends JDKAlgorithmParameters
++    // {
++    //     ElGamalParameterSpec     currentSpec;
++    //
++    //     /**
++    //      * Return the X.509 ASN.1 structure ElGamalParameter.
++    //      * <p>
++    //      * <pre>
++    //      *  ElGamalParameter ::= SEQUENCE {
++    //      *                   prime INTEGER, -- p
++    //      *                   base INTEGER, -- g}
++    //      * </pre>
++    //      */
++    //     protected byte[] engineGetEncoded() 
++    //     {
++    //         ElGamalParameter elP = new ElGamalParameter(currentSpec.getP(), currentSpec.getG());
++    //
++    //         try
++    //         {
++    //             return elP.getEncoded(ASN1Encodable.DER);
++    //         }
++    //         catch (IOException e)
++    //         {
++    //             throw new RuntimeException("Error encoding ElGamalParameters");
++    //         }
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //         String format) 
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //         Class paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == ElGamalParameterSpec.class)
++    //         {
++    //             return currentSpec;
++    //         }
++    //         else if (paramSpec == DHParameterSpec.class)
++    //         {
++    //             return new DHParameterSpec(currentSpec.getP(), currentSpec.getG());
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (!(paramSpec instanceof ElGamalParameterSpec) && !(paramSpec instanceof DHParameterSpec))
++    //         {
++    //             throw new InvalidParameterSpecException("DHParameterSpec required to initialise a ElGamal algorithm parameters object");
++    //         }
++    //
++    //         if (paramSpec instanceof ElGamalParameterSpec)
++    //         {
++    //             this.currentSpec = (ElGamalParameterSpec)paramSpec;
++    //         }
++    //         else
++    //         {
++    //             DHParameterSpec s = (DHParameterSpec)paramSpec;
++    //
++    //             this.currentSpec = new ElGamalParameterSpec(s.getP(), s.getG());
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params) 
++    //         throws IOException
++    //     {
++    //         try
++    //         {
++    //             ElGamalParameter elP = new ElGamalParameter((ASN1Sequence)ASN1Object.fromByteArray(params));
++    //
++    //             currentSpec = new ElGamalParameterSpec(elP.getP(), elP.getG());
++    //         }
++    //         catch (ClassCastException e)
++    //         {
++    //             throw new IOException("Not a valid ElGamal Parameter encoding.");
++    //         }
++    //         catch (ArrayIndexOutOfBoundsException e)
++    //         {
++    //             throw new IOException("Not a valid ElGamal Parameter encoding.");
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params,
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             engineInit(params);
++    //         }
++    //         else
++    //         {
++    //             throw new IOException("Unknown parameter format " + format);
++    //         }
++    //     }
++    //
++    //     protected String engineToString() 
++    //     {
++    //         return "ElGamal Parameters";
++    //     }
++    // }
++    //
++    // public static class IES
++    //     extends JDKAlgorithmParameters
++    // {
++    //     IESParameterSpec     currentSpec;
++    //
++    //     /**
++    //      * in the absence of a standard way of doing it this will do for
++    //      * now...
++    //      */
++    //     protected byte[] engineGetEncoded() 
++    //     {
++    //         try
++    //         {
++    //             ASN1EncodableVector v = new ASN1EncodableVector();
++    //
++    //             v.add(new DEROctetString(currentSpec.getDerivationV()));
++    //             v.add(new DEROctetString(currentSpec.getEncodingV()));
++    //             v.add(new DERInteger(currentSpec.getMacKeySize()));
++    //
++    //             return new DERSequence(v).getEncoded(ASN1Encodable.DER);
++    //         }
++    //         catch (IOException e)
++    //         {
++    //             throw new RuntimeException("Error encoding IESParameters");
++    //         }
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //         String format) 
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //         Class paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == IESParameterSpec.class)
++    //         {
++    //             return currentSpec;
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (!(paramSpec instanceof IESParameterSpec))
++    //         {
++    //             throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object");
++    //         }
++    //
++    //         this.currentSpec = (IESParameterSpec)paramSpec;
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params) 
++    //         throws IOException
++    //     {
++    //         try
++    //         {
++    //             ASN1Sequence s = (ASN1Sequence)ASN1Object.fromByteArray(params);
++    //
++    //             this.currentSpec = new IESParameterSpec(
++    //                                     ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
++    //                                     ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
++    //                                     ((DERInteger)s.getObjectAt(0)).getValue().intValue());
++    //         }
++    //         catch (ClassCastException e)
++    //         {
++    //             throw new IOException("Not a valid IES Parameter encoding.");
++    //         }
++    //         catch (ArrayIndexOutOfBoundsException e)
++    //         {
++    //             throw new IOException("Not a valid IES Parameter encoding.");
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params,
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             engineInit(params);
++    //         }
++    //         else
++    //         {
++    //             throw new IOException("Unknown parameter format " + format);
++    //         }
++    //     }
++    //
++    //     protected String engineToString() 
++    //     {
++    //         return "IES Parameters";
++    //     }
++    // }
++    // END android-removed
++    
++    public static class OAEP
+         extends JDKAlgorithmParameters
+     {
+-        GOST3410ParameterSpec     currentSpec;
+-        
++        OAEPParameterSpec     currentSpec;
++    
+         /**
+-         * Return the X.509 ASN.1 structure GOST3410Parameter.
+-         * <p>
+-         * <pre>
+-         *  GOST3410Parameter ::= SEQUENCE {
+-         *                   prime INTEGER, -- p
+-         *                   subprime INTEGER, -- q
+-         *                   base INTEGER, -- a}
+-         * </pre>
++         * Return the PKCS#1 ASN.1 structure RSAES-OAEP-params.
+          */
+-        protected byte[] engineGetEncoded()
++        protected byte[] engineGetEncoded() 
+         {
+-            GOST3410PublicKeyAlgParameters gost3410P = new GOST3410PublicKeyAlgParameters(new DERObjectIdentifier(currentSpec.getPublicKeyParamSetOID()), new DERObjectIdentifier(currentSpec.getDigestParamSetOID()), new DERObjectIdentifier(currentSpec.getEncryptionParamSetOID()));
+-
++            AlgorithmIdentifier     hashAlgorithm = new AlgorithmIdentifier(
++                                                            JCEDigestUtil.getOID(currentSpec.getDigestAlgorithm()),
++                                                            // BEGIN android-changed
++                                                            DERNull.INSTANCE);
++                                                            // END android-changed
++            MGF1ParameterSpec       mgfSpec = (MGF1ParameterSpec)currentSpec.getMGFParameters();
++            AlgorithmIdentifier     maskGenAlgorithm = new AlgorithmIdentifier(
++                                                            PKCSObjectIdentifiers.id_mgf1, 
++                                                            // BEGIN android-changed
++                                                            new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
++                                                            // END android-changed
++            PSource.PSpecified      pSource = (PSource.PSpecified)currentSpec.getPSource();
++            AlgorithmIdentifier     pSourceAlgorithm = new AlgorithmIdentifier(
++                                                            PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(pSource.getValue()));
++            RSAESOAEPparams         oaepP = new RSAESOAEPparams(hashAlgorithm, maskGenAlgorithm, pSourceAlgorithm);
++    
+             try
+             {
+-                return gost3410P.getEncoded(ASN1Encodable.DER);
++                return oaepP.getEncoded(ASN1Encodable.DER);
+             }
+             catch (IOException e)
+             {
+-                throw new RuntimeException("Error encoding GOST3410Parameters");
++                throw new RuntimeException("Error encoding OAEPParameters");
+             }
+         }
+-        
++    
+         protected byte[] engineGetEncoded(
+-                String format)
++            String format) 
+         {
+             if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+             {
+                 return engineGetEncoded();
+             }
+-            
++    
+             return null;
+         }
+-        
++    
+         protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-                Class paramSpec)
+-        throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == GOST3410PublicKeyParameterSetSpec.class)
+-            {
+-                return currentSpec;
+-            }
+-            
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to GOST3410 parameters object.");
+-        }
+-        
+-        protected void engineInit(
+-                AlgorithmParameterSpec paramSpec)
+-        throws InvalidParameterSpecException
+-        {
+-            if (!(paramSpec instanceof GOST3410ParameterSpec))
+-            {
+-                throw new InvalidParameterSpecException("GOST3410ParameterSpec required to initialise a GOST3410 algorithm parameters object");
+-            }
+-            
+-            this.currentSpec = (GOST3410ParameterSpec)paramSpec;
+-        }
+-        
+-        protected void engineInit(
+-                byte[] params)
+-        throws IOException
+-        {
+-            try
+-            {
+-                ASN1Sequence seq = (ASN1Sequence) ASN1Object.fromByteArray(params);
+-
+-                this.currentSpec = GOST3410ParameterSpec.fromPublicKeyAlg(
+-                    new GOST3410PublicKeyAlgParameters(seq));
+-            }
+-            catch (ClassCastException e)
+-            {
+-                throw new IOException("Not a valid GOST3410 Parameter encoding.");
+-            }
+-            catch (ArrayIndexOutOfBoundsException e)
+-            {
+-                throw new IOException("Not a valid GOST3410 Parameter encoding.");
+-            }
+-        }
+-        
+-        protected void engineInit(
+-                byte[] params,
+-                String format)
+-        throws IOException
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                engineInit(params);
+-            }
+-            else
+-            {
+-                throw new IOException("Unknown parameter format " + format);
+-            }
+-        }
+-        
+-        protected String engineToString()
+-        {
+-            return "GOST3410 Parameters";
+-        }
+-    }
+-
+-    public static class ElGamal
+-        extends JDKAlgorithmParameters
+-    {
+-        ElGamalParameterSpec     currentSpec;
+-
+-        /**
+-         * Return the X.509 ASN.1 structure ElGamalParameter.
+-         * <p>
+-         * <pre>
+-         *  ElGamalParameter ::= SEQUENCE {
+-         *                   prime INTEGER, -- p
+-         *                   base INTEGER, -- g}
+-         * </pre>
+-         */
+-        protected byte[] engineGetEncoded() 
+-        {
+-            ElGamalParameter elP = new ElGamalParameter(currentSpec.getP(), currentSpec.getG());
+-
+-            try
+-            {
+-                return elP.getEncoded(ASN1Encodable.DER);
+-            }
+-            catch (IOException e)
+-            {
+-                throw new RuntimeException("Error encoding ElGamalParameters");
+-            }
+-        }
+-
+-        protected byte[] engineGetEncoded(
+-            String format) 
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                return engineGetEncoded();
+-            }
+-
+-            return null;
+-        }
+-
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == ElGamalParameterSpec.class)
+-            {
+-                return currentSpec;
+-            }
+-            else if (paramSpec == DHParameterSpec.class)
+-            {
+-                return new DHParameterSpec(currentSpec.getP(), currentSpec.getG());
+-            }
+-
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
+-        }
+-
+-        protected void engineInit(
+-            AlgorithmParameterSpec paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (!(paramSpec instanceof ElGamalParameterSpec) && !(paramSpec instanceof DHParameterSpec))
+-            {
+-                throw new InvalidParameterSpecException("DHParameterSpec required to initialise a ElGamal algorithm parameters object");
+-            }
+-
+-            if (paramSpec instanceof ElGamalParameterSpec)
+-            {
+-                this.currentSpec = (ElGamalParameterSpec)paramSpec;
+-            }
+-            else
+-            {
+-                DHParameterSpec s = (DHParameterSpec)paramSpec;
+-                
+-                this.currentSpec = new ElGamalParameterSpec(s.getP(), s.getG());
+-            }
+-        }
+-
+-        protected void engineInit(
+-            byte[] params) 
+-            throws IOException
+-        {
+-            try
+-            {
+-                ElGamalParameter elP = new ElGamalParameter((ASN1Sequence)ASN1Object.fromByteArray(params));
+-
+-                currentSpec = new ElGamalParameterSpec(elP.getP(), elP.getG());
+-            }
+-            catch (ClassCastException e)
+-            {
+-                throw new IOException("Not a valid ElGamal Parameter encoding.");
+-            }
+-            catch (ArrayIndexOutOfBoundsException e)
+-            {
+-                throw new IOException("Not a valid ElGamal Parameter encoding.");
+-            }
+-        }
+-
+-        protected void engineInit(
+-            byte[] params,
+-            String format) 
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                engineInit(params);
+-            }
+-            else
+-            {
+-                throw new IOException("Unknown parameter format " + format);
+-            }
+-        }
+-
+-        protected String engineToString() 
+-        {
+-            return "ElGamal Parameters";
+-        }
+-    }
+-
+-    public static class IES
+-        extends JDKAlgorithmParameters
+-    {
+-        IESParameterSpec     currentSpec;
+-
+-        /**
+-         * in the absence of a standard way of doing it this will do for
+-         * now...
+-         */
+-        protected byte[] engineGetEncoded() 
+-        {
+-            try
+-            {
+-                ASN1EncodableVector v = new ASN1EncodableVector();
+-
+-                v.add(new DEROctetString(currentSpec.getDerivationV()));
+-                v.add(new DEROctetString(currentSpec.getEncodingV()));
+-                v.add(new DERInteger(currentSpec.getMacKeySize()));
+-
+-                return new DERSequence(v).getEncoded(ASN1Encodable.DER);
+-            }
+-            catch (IOException e)
+-            {
+-                throw new RuntimeException("Error encoding IESParameters");
+-            }
+-        }
+-
+-        protected byte[] engineGetEncoded(
+-            String format) 
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                return engineGetEncoded();
+-            }
+-
+-            return null;
+-        }
+-
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == IESParameterSpec.class)
+-            {
+-                return currentSpec;
+-            }
+-
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
+-        }
+-
+-        protected void engineInit(
+-            AlgorithmParameterSpec paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (!(paramSpec instanceof IESParameterSpec))
+-            {
+-                throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object");
+-            }
+-
+-            this.currentSpec = (IESParameterSpec)paramSpec;
+-        }
+-
+-        protected void engineInit(
+-            byte[] params) 
+-            throws IOException
+-        {
+-            try
+-            {
+-                ASN1Sequence s = (ASN1Sequence)ASN1Object.fromByteArray(params);
+-
+-                this.currentSpec = new IESParameterSpec(
+-                                        ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+-                                        ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+-                                        ((DERInteger)s.getObjectAt(0)).getValue().intValue());
+-            }
+-            catch (ClassCastException e)
+-            {
+-                throw new IOException("Not a valid IES Parameter encoding.");
+-            }
+-            catch (ArrayIndexOutOfBoundsException e)
+-            {
+-                throw new IOException("Not a valid IES Parameter encoding.");
+-            }
+-        }
+-
+-        protected void engineInit(
+-            byte[] params,
+-            String format) 
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                engineInit(params);
+-            }
+-            else
+-            {
+-                throw new IOException("Unknown parameter format " + format);
+-            }
+-        }
+-
+-        protected String engineToString() 
+-        {
+-            return "IES Parameters";
+-        }
+-    }
+-    
+-    public static class OAEP
+-        extends JDKAlgorithmParameters
+-    {
+-        OAEPParameterSpec     currentSpec;
+-    
+-        /**
+-         * Return the PKCS#1 ASN.1 structure RSAES-OAEP-params.
+-         */
+-        protected byte[] engineGetEncoded() 
+-        {
+-            AlgorithmIdentifier     hashAlgorithm = new AlgorithmIdentifier(
+-                                                            JCEDigestUtil.getOID(currentSpec.getDigestAlgorithm()),
+-                                                            new DERNull());
+-            MGF1ParameterSpec       mgfSpec = (MGF1ParameterSpec)currentSpec.getMGFParameters();
+-            AlgorithmIdentifier     maskGenAlgorithm = new AlgorithmIdentifier(
+-                                                            PKCSObjectIdentifiers.id_mgf1, 
+-                                                            new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), new DERNull()));
+-            PSource.PSpecified      pSource = (PSource.PSpecified)currentSpec.getPSource();
+-            AlgorithmIdentifier     pSourceAlgorithm = new AlgorithmIdentifier(
+-                                                            PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(pSource.getValue()));
+-            RSAESOAEPparams         oaepP = new RSAESOAEPparams(hashAlgorithm, maskGenAlgorithm, pSourceAlgorithm);
+-    
+-            try
+-            {
+-                return oaepP.getEncoded(ASN1Encodable.DER);
+-            }
+-            catch (IOException e)
+-            {
+-                throw new RuntimeException("Error encoding OAEPParameters");
+-            }
+-        }
+-    
+-        protected byte[] engineGetEncoded(
+-            String format) 
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                return engineGetEncoded();
+-            }
+-    
+-            return null;
+-        }
+-    
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec) 
+-            throws InvalidParameterSpecException
++            Class paramSpec) 
++            throws InvalidParameterSpecException
+         {
+             if (paramSpec == OAEPParameterSpec.class && currentSpec != null)
+             {
+@@ -1167,110 +1185,116 @@
+         }
+     }
+     
+-    public static class PSS
+-        extends JDKAlgorithmParameters
+-    {  
+-        PSSParameterSpec     currentSpec;
+-    
+-        /**
+-         * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params.
+-         */
+-        protected byte[] engineGetEncoded() 
+-            throws IOException
+-        {
+-            PSSParameterSpec    pssSpec = currentSpec;
+-            AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
+-                                                JCEDigestUtil.getOID(pssSpec.getDigestAlgorithm()),
+-                                                new DERNull());
+-            MGF1ParameterSpec   mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
+-            AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+-                                                PKCSObjectIdentifiers.id_mgf1, 
+-                                                new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), new DERNull()));
+-            RSASSAPSSparams     pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new DERInteger(pssSpec.getSaltLength()), new DERInteger(pssSpec.getTrailerField()));
+-            
+-            return pssP.getEncoded("DER");
+-        }
+-    
+-        protected byte[] engineGetEncoded(
+-            String format) 
+-            throws IOException
+-        {
+-            if (format.equalsIgnoreCase("X.509")
+-                    || format.equalsIgnoreCase("ASN.1"))
+-            {
+-                return engineGetEncoded();
+-            }
+-    
+-            return null;
+-        }
+-    
+-        protected AlgorithmParameterSpec localEngineGetParameterSpec(
+-            Class paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (paramSpec == PSSParameterSpec.class && currentSpec != null)
+-            {
+-                return currentSpec;
+-            }
+-    
+-            throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
+-        }
+-    
+-        protected void engineInit(
+-            AlgorithmParameterSpec paramSpec) 
+-            throws InvalidParameterSpecException
+-        {
+-            if (!(paramSpec instanceof PSSParameterSpec))
+-            {
+-                throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object");
+-            }
+-    
+-            this.currentSpec = (PSSParameterSpec)paramSpec;
+-        }
+-    
+-        protected void engineInit(
+-            byte[] params) 
+-            throws IOException
+-        {
+-            try
+-            {
+-                RSASSAPSSparams pssP = new RSASSAPSSparams((ASN1Sequence)ASN1Object.fromByteArray(params));
+-
+-                currentSpec = new PSSParameterSpec(
+-                                       pssP.getHashAlgorithm().getObjectId().getId(), 
+-                                       pssP.getMaskGenAlgorithm().getObjectId().getId(), 
+-                                       new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getObjectId().getId()),
+-                                       pssP.getSaltLength().getValue().intValue(),
+-                                       pssP.getTrailerField().getValue().intValue());
+-            }
+-            catch (ClassCastException e)
+-            {
+-                throw new IOException("Not a valid PSS Parameter encoding.");
+-            }
+-            catch (ArrayIndexOutOfBoundsException e)
+-            {
+-                throw new IOException("Not a valid PSS Parameter encoding.");
+-            }
+-        }
+-    
+-        protected void engineInit(
+-            byte[] params,
+-            String format) 
+-            throws IOException
+-        {
+-            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+-            {
+-                engineInit(params);
+-            }
+-            else
+-            {
+-                throw new IOException("Unknown parameter format " + format);
+-            }
+-        }
+-    
+-        protected String engineToString() 
+-        {
+-            return "PSS Parameters";
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class PSS
++    //     extends JDKAlgorithmParameters
++    // {  
++    //     PSSParameterSpec     currentSpec;
++    //
++    //     /**
++    //      * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params.
++    //      */
++    //     protected byte[] engineGetEncoded() 
++    //         throws IOException
++    //     {
++    //         PSSParameterSpec    pssSpec = currentSpec;
++    //         AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
++    //                                             JCEDigestUtil.getOID(pssSpec.getDigestAlgorithm()),
++    //                                             // BEGIN android-changed
++    //                                             DERNull.INSTANCE);
++    //                                             // END android-changed
++    //         MGF1ParameterSpec   mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
++    //         AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
++    //                                             PKCSObjectIdentifiers.id_mgf1, 
++    //                                             // BEGIN android-changed
++    //                                             new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
++    //                                             // END android-changed
++    //         RSASSAPSSparams     pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new DERInteger(pssSpec.getSaltLength()), new DERInteger(pssSpec.getTrailerField()));
++    //
++    //         return pssP.getEncoded("DER");
++    //     }
++    //
++    //     protected byte[] engineGetEncoded(
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (format.equalsIgnoreCase("X.509")
++    //                 || format.equalsIgnoreCase("ASN.1"))
++    //         {
++    //             return engineGetEncoded();
++    //         }
++    //
++    //         return null;
++    //     }
++    //
++    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
++    //         Class paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (paramSpec == PSSParameterSpec.class && currentSpec != null)
++    //         {
++    //             return currentSpec;
++    //         }
++    //
++    //         throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
++    //     }
++    //
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec paramSpec) 
++    //         throws InvalidParameterSpecException
++    //     {
++    //         if (!(paramSpec instanceof PSSParameterSpec))
++    //         {
++    //             throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object");
++    //         }
++    //
++    //         this.currentSpec = (PSSParameterSpec)paramSpec;
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params) 
++    //         throws IOException
++    //     {
++    //         try
++    //         {
++    //             RSASSAPSSparams pssP = new RSASSAPSSparams((ASN1Sequence)ASN1Object.fromByteArray(params));
++    //
++    //             currentSpec = new PSSParameterSpec(
++    //                                    pssP.getHashAlgorithm().getObjectId().getId(), 
++    //                                    pssP.getMaskGenAlgorithm().getObjectId().getId(), 
++    //                                    new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getObjectId().getId()),
++    //                                    pssP.getSaltLength().getValue().intValue(),
++    //                                    pssP.getTrailerField().getValue().intValue());
++    //         }
++    //         catch (ClassCastException e)
++    //         {
++    //             throw new IOException("Not a valid PSS Parameter encoding.");
++    //         }
++    //         catch (ArrayIndexOutOfBoundsException e)
++    //         {
++    //             throw new IOException("Not a valid PSS Parameter encoding.");
++    //         }
++    //     }
++    //
++    //     protected void engineInit(
++    //         byte[] params,
++    //         String format) 
++    //         throws IOException
++    //     {
++    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
++    //         {
++    //             engineInit(params);
++    //         }
++    //         else
++    //         {
++    //             throw new IOException("Unknown parameter format " + format);
++    //         }
++    //     }
++    //
++    //     protected String engineToString() 
++    //     {
++    //         return "PSS Parameters";
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKDSASigner.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKDSASigner.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKDSASigner.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKDSASigner.java	2010-06-25 23:17:26.000000000 +0000
+@@ -22,13 +22,17 @@
+ import org.bouncycastle.crypto.DSA;
+ import org.bouncycastle.crypto.Digest;
+ import org.bouncycastle.crypto.digests.SHA1Digest;
+-import org.bouncycastle.crypto.digests.SHA224Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.SHA224Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA256Digest;
+ import org.bouncycastle.crypto.digests.SHA384Digest;
+ import org.bouncycastle.crypto.digests.SHA512Digest;
+ import org.bouncycastle.crypto.params.ParametersWithRandom;
+ import org.bouncycastle.crypto.signers.DSASigner;
+-import org.bouncycastle.jce.interfaces.GOST3410Key;
++// BEGIN android-removed
++// import org.bouncycastle.jce.interfaces.GOST3410Key;
++// END android-removed
+ import org.bouncycastle.jce.provider.util.NullDigest;
+ 
+ public class JDKDSASigner
+@@ -53,11 +57,16 @@
+     {
+         CipherParameters    param;
+ 
+-        if (publicKey instanceof GOST3410Key)
+-        {
+-            param = GOST3410Util.generatePublicKeyParameter(publicKey);
+-        }
+-        else if (publicKey instanceof DSAKey)
++        // BEGIN android-removed
++        // if (publicKey instanceof GOST3410Key)
++        // {
++        //     param = GOST3410Util.generatePublicKeyParameter(publicKey);
++        // }
++        // else if (publicKey instanceof DSAKey)
++        // END android-removed
++        // BEGIN android-added
++        if (publicKey instanceof DSAKey)
++        // END android-added
+         {
+             param = DSAUtil.generatePublicKeyParameter(publicKey);
+         }
+@@ -103,14 +112,18 @@
+     {
+         CipherParameters    param;
+ 
+-        if (privateKey instanceof GOST3410Key)
+-        {
+-            param = GOST3410Util.generatePrivateKeyParameter(privateKey);
+-        }
+-        else
+-        {
++        // BEGIN android-removed
++        // if (privateKey instanceof GOST3410Key)
++        // {
++        //     param = GOST3410Util.generatePrivateKeyParameter(privateKey);
++        // }
++        // else
++        // {
++        // END android-removed
+             param = DSAUtil.generatePrivateKeyParameter(privateKey);
+-        }
++        // BEGIN android-removed
++        // }
++        // END android-removed
+ 
+         if (random != null)
+         {
+@@ -231,42 +244,44 @@
+             super(new SHA1Digest(), new DSASigner());
+         }
+     }
+-
+-    static public class dsa224
+-        extends JDKDSASigner
+-    {
+-        public dsa224()
+-        {
+-            super(new SHA224Digest(), new DSASigner());
+-        }
+-    }
+-    
+-    static public class dsa256
+-        extends JDKDSASigner
+-    {
+-        public dsa256()
+-        {
+-            super(new SHA256Digest(), new DSASigner());
+-        }
+-    }
+     
+-    static public class dsa384
+-        extends JDKDSASigner
+-    {
+-        public dsa384()
+-        {
+-            super(new SHA384Digest(), new DSASigner());
+-        }
+-    }
+-    
+-    static public class dsa512
+-        extends JDKDSASigner
+-    {
+-        public dsa512()
+-        {
+-            super(new SHA512Digest(), new DSASigner());
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class dsa224
++    //     extends JDKDSASigner
++    // {
++    //     public dsa224()
++    //     {
++    //         super(new SHA224Digest(), new DSASigner());
++    //     }
++    // }
++    //
++    // static public class dsa256
++    //     extends JDKDSASigner
++    // {
++    //     public dsa256()
++    //     {
++    //         super(new SHA256Digest(), new DSASigner());
++    //     }
++    // }
++    //
++    // static public class dsa384
++    //     extends JDKDSASigner
++    // {
++    //     public dsa384()
++    //     {
++    //         super(new SHA384Digest(), new DSASigner());
++    //     }
++    // }
++    //
++    // static public class dsa512
++    //     extends JDKDSASigner
++    // {
++    //     public dsa512()
++    //     {
++    //         super(new SHA512Digest(), new DSASigner());
++    //     }
++    // }
++    // END android-removed
+ 
+     static public class noneDSA
+         extends JDKDSASigner
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKDigestSignature.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKDigestSignature.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKDigestSignature.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKDigestSignature.java	2010-06-25 23:17:26.000000000 +0000
+@@ -23,14 +23,20 @@
+ import org.bouncycastle.crypto.AsymmetricBlockCipher;
+ import org.bouncycastle.crypto.CipherParameters;
+ import org.bouncycastle.crypto.Digest;
+-import org.bouncycastle.crypto.digests.MD2Digest;
+-import org.bouncycastle.crypto.digests.MD4Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.MD2Digest;
++// import org.bouncycastle.crypto.digests.MD4Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.MD5Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD256Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
++// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
++// import org.bouncycastle.crypto.digests.RIPEMD256Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA1Digest;
+-import org.bouncycastle.crypto.digests.SHA224Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.SHA224Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA256Digest;
+ import org.bouncycastle.crypto.digests.SHA384Digest;
+ import org.bouncycastle.crypto.digests.SHA512Digest;
+@@ -179,13 +185,13 @@
+                 }
+             }
+         }
+-        else if (sig.length == expected.length - 2)  // NULL left out
++        else if (expected.length == sig.length - 2)  // NULL left out
+         {
+             int sigOffset = sig.length - hash.length - 2;
+             int expectedOffset = expected.length - hash.length - 2;
+ 
+-            expected[1] -= 2;      // adjust lengths
+-            expected[3] -= 2;
++            sig[1] -= 2;      // adjust lengths
++            sig[3] -= 2;
+ 
+             for (int i = 0; i < hash.length; i++)
+             {
+@@ -195,7 +201,7 @@
+                 }
+             }
+ 
+-            for (int i = 0; i < sigOffset; i++)
++            for (int i = 0; i < expectedOffset; i++)
+             {
+                 if (sig[i] != expected[i])  // check header less NULL
+                 {
+@@ -265,14 +271,16 @@
+         }
+     }
+ 
+-    static public class SHA224WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public SHA224WithRSAEncryption()
+-        {
+-            super(NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class SHA224WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public SHA224WithRSAEncryption()
++    //     {
++    //         super(NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    // END android-removed
+ 
+     static public class SHA256WithRSAEncryption
+         extends JDKDigestSignature
+@@ -301,23 +309,25 @@
+         }
+     }
+ 
+-    static public class MD2WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public MD2WithRSAEncryption()
+-        {
+-            super(PKCSObjectIdentifiers.md2, new MD2Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class MD4WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public MD4WithRSAEncryption()
+-        {
+-            super(PKCSObjectIdentifiers.md4, new MD4Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class MD2WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public MD2WithRSAEncryption()
++    //     {
++    //         super(PKCSObjectIdentifiers.md2, new MD2Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class MD4WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public MD4WithRSAEncryption()
++    //     {
++    //         super(PKCSObjectIdentifiers.md4, new MD4Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    // END android-removed
+ 
+     static public class MD5WithRSAEncryption
+         extends JDKDigestSignature
+@@ -328,39 +338,41 @@
+         }
+     }
+ 
+-    static public class RIPEMD160WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public RIPEMD160WithRSAEncryption()
+-        {
+-            super(TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class RIPEMD128WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public RIPEMD128WithRSAEncryption()
+-        {
+-            super(TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class RIPEMD256WithRSAEncryption
+-        extends JDKDigestSignature
+-    {
+-        public RIPEMD256WithRSAEncryption()
+-        {
+-            super(TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
+-
+-    static public class noneRSA
+-        extends JDKDigestSignature
+-    {
+-        public noneRSA()
+-        {
+-            super(new NullDigest(), new PKCS1Encoding(new RSABlindedEngine()));
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class RIPEMD160WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public RIPEMD160WithRSAEncryption()
++    //     {
++    //         super(TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class RIPEMD128WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public RIPEMD128WithRSAEncryption()
++    //     {
++    //         super(TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class RIPEMD256WithRSAEncryption
++    //     extends JDKDigestSignature
++    // {
++    //     public RIPEMD256WithRSAEncryption()
++    //     {
++    //         super(TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    //
++    // static public class noneRSA
++    //     extends JDKDigestSignature
++    // {
++    //     public noneRSA()
++    //     {
++    //         super(new NullDigest(), new PKCS1Encoding(new RSABlindedEngine()));
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyFactory.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyFactory.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyFactory.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyFactory.java	2010-06-25 23:17:26.000000000 +0000
+@@ -36,17 +36,21 @@
+ import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
+ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+ import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+-import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
+-import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
+-import org.bouncycastle.jce.spec.ElGamalPrivateKeySpec;
+-import org.bouncycastle.jce.spec.ElGamalPublicKeySpec;
+-import org.bouncycastle.jce.spec.GOST3410PrivateKeySpec;
+-import org.bouncycastle.jce.spec.GOST3410PublicKeySpec;
++// BEGIN android-removed
++// import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
++// import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
++// import org.bouncycastle.jce.spec.ElGamalPrivateKeySpec;
++// import org.bouncycastle.jce.spec.ElGamalPublicKeySpec;
++// import org.bouncycastle.jce.spec.GOST3410PrivateKeySpec;
++// import org.bouncycastle.jce.spec.GOST3410PublicKeySpec;
++// END android-removed
+ 
+ public abstract class JDKKeyFactory
+     extends KeyFactorySpi
+ {
+-    protected boolean elGamalFactory = false;
++    // BEGIN android-removed
++    // protected boolean elGamalFactory = false;
++    // END android-removed
+     
+     public JDKKeyFactory()
+     {
+@@ -162,25 +166,33 @@
+         }
+         else if (key instanceof DHPublicKey)
+         {
+-            if (elGamalFactory)
+-            {
+-                return new JCEElGamalPublicKey((DHPublicKey)key);
+-            }
+-            else
+-            {
++            // BEGIN android-removed
++            // if (elGamalFactory)
++            // {
++            //     return new JCEElGamalPublicKey((DHPublicKey)key);
++            // }
++            // else
++            // {
++            // END android-removed
+                 return new JCEDHPublicKey((DHPublicKey)key);
+-            }
++            // BEGIN android-removed
++            // }
++            // END android-removed
+         }
+         else if (key instanceof DHPrivateKey)
+         {
+-            if (elGamalFactory)
+-            {
+-                return new JCEElGamalPrivateKey((DHPrivateKey)key);
+-            }
+-            else
+-            {
++            // BEGIN android-removed
++            // if (elGamalFactory)
++            // {
++            //     return new JCEElGamalPrivateKey((DHPrivateKey)key);
++            // }
++            // else
++            // {
++            // END android-removed
+                 return new JCEDHPrivateKey((DHPrivateKey)key);
+-            }
++            // BEGIN android-removed
++            // }
++            // END android-removed
+         }
+         else if (key instanceof DSAPublicKey)
+         {
+@@ -190,14 +202,16 @@
+         {
+             return new JDKDSAPrivateKey((DSAPrivateKey)key);
+         }
+-        else if (key instanceof ElGamalPublicKey)
+-        {
+-            return new JCEElGamalPublicKey((ElGamalPublicKey)key);
+-        }
+-        else if (key instanceof ElGamalPrivateKey)
+-        {
+-            return new JCEElGamalPrivateKey((ElGamalPrivateKey)key);
+-        }
++        // BEGIN android-removed
++        // else if (key instanceof ElGamalPublicKey)
++        // {
++        //     return new JCEElGamalPublicKey((ElGamalPublicKey)key);
++        // }
++        // else if (key instanceof ElGamalPrivateKey)
++        // {
++        //    return new JCEElGamalPrivateKey((ElGamalPrivateKey)key);
++        // }
++        // END android-removed
+ 
+         throw new InvalidKeyException("key type unknown");
+     }
+@@ -233,10 +247,12 @@
+         {
+             return new JCEDHPublicKey(info);
+         }
+-        else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm))
+-        {
+-            return new JCEElGamalPublicKey(info);
+-        }
++        // BEGIN android-removed
++        // else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm))
++        // {
++        //     return new JCEElGamalPublicKey(info);
++        // }
++        // END android-removed
+         else if (algOid.equals(X9ObjectIdentifiers.id_dsa))
+         {
+             return new JDKDSAPublicKey(info);
+@@ -245,18 +261,19 @@
+         {
+             return new JDKDSAPublicKey(info);
+         }
+-        else if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey))
+-        {
+-            return new JCEECPublicKey(info);
+-        }
+-        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94))
+-        {
+-            return new JDKGOST3410PublicKey(info);
+-        }
+-        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001))
+-        {
+-            return new JCEECPublicKey(info);
+-        }
++        // BEGIN android-removed
++        // else if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey))
++        // {
++        //     return new JCEECPublicKey(info);
++        // }
++        // else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94))
++        // {
++        //     return new JDKGOST3410PublicKey(info);
++        // }
++        // else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001))
++        // {
++        //     return new JCEECPublicKey(info);
++        // }
+         else
+         {
+             throw new RuntimeException("algorithm identifier " + algOid + " in key not recognised");
+@@ -290,26 +307,30 @@
+         {
+               return new JCEDHPrivateKey(info);
+         }
+-        else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm))
+-        {
+-              return new JCEElGamalPrivateKey(info);
+-        }
++        // BEGIN android-removed
++        // else if (algOid.equals(OIWObjectIdentifiers.elGamalAlgorithm))
++        // {
++        //       return new JCEElGamalPrivateKey(info);
++        // }
++        // END android-removed
+         else if (algOid.equals(X9ObjectIdentifiers.id_dsa))
+         {
+               return new JDKDSAPrivateKey(info);
+         }
+-        else if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey))
+-        {
+-              return new JCEECPrivateKey(info);
+-        }
+-        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94))
+-        {
+-              return new JDKGOST3410PrivateKey(info);
+-        }
+-        else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001))
+-        {
+-              return new JCEECPrivateKey(info);
+-        }
++        // BEGIN android-removed
++        // else if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey))
++        // {
++        //       return new JCEECPrivateKey(info);
++        // }
++        // else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_94))
++        // {
++        //       return new JDKGOST3410PrivateKey(info);
++        // }
++        // else if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001))
++        // {
++        //       return new JCEECPrivateKey(info);
++        // }
++        // END android-removed
+         else
+         {
+             throw new RuntimeException("algorithm identifier " + algOid + " in key not recognised");
+@@ -440,89 +461,92 @@
+         }
+     }
+ 
+-    public static class GOST3410
+-        extends JDKKeyFactory
+-    {
+-        public GOST3410()
+-        {
+-        }
+-        
+-        protected PrivateKey engineGeneratePrivate(
+-                KeySpec    keySpec)
+-        throws InvalidKeySpecException
+-        {
+-            if (keySpec instanceof GOST3410PrivateKeySpec)
+-            {
+-                return new JDKGOST3410PrivateKey((GOST3410PrivateKeySpec)keySpec);
+-            }
+-
+-            return super.engineGeneratePrivate(keySpec);
+-        }
+-        
+-        protected PublicKey engineGeneratePublic(
+-                KeySpec    keySpec)
+-        throws InvalidKeySpecException
+-        {
+-            if (keySpec instanceof GOST3410PublicKeySpec)
+-            {
+-                return new JDKGOST3410PublicKey((GOST3410PublicKeySpec)keySpec);
+-            }
+-
+-            return super.engineGeneratePublic(keySpec);
+-        }
+-    }
+-    
+-    public static class ElGamal
+-        extends JDKKeyFactory
+-    {
+-        public ElGamal()
+-        {
+-            elGamalFactory = true;
+-        }
+-
+-        protected PrivateKey engineGeneratePrivate(
+-            KeySpec    keySpec)
+-            throws InvalidKeySpecException
+-        {
+-            if (keySpec instanceof ElGamalPrivateKeySpec)
+-            {
+-                return new JCEElGamalPrivateKey((ElGamalPrivateKeySpec)keySpec);
+-            }
+-            else if (keySpec instanceof DHPrivateKeySpec)
+-            {
+-                return new JCEElGamalPrivateKey((DHPrivateKeySpec)keySpec);
+-            }
+-
+-            return super.engineGeneratePrivate(keySpec);
+-        }
++    // BEGIN android-removed
++    // public static class GOST3410
++    //     extends JDKKeyFactory
++    // {
++    //     public GOST3410()
++    //     {
++    //     }
++    //
++    //     protected PrivateKey engineGeneratePrivate(
++    //             KeySpec    keySpec)
++    //     throws InvalidKeySpecException
++    //     {
++    //         if (keySpec instanceof GOST3410PrivateKeySpec)
++    //         {
++    //             return new JDKGOST3410PrivateKey((GOST3410PrivateKeySpec)keySpec);
++    //         }
++    //
++    //         return super.engineGeneratePrivate(keySpec);
++    //     }
++    //
++    //     protected PublicKey engineGeneratePublic(
++    //             KeySpec    keySpec)
++    //     throws InvalidKeySpecException
++    //     {
++    //         if (keySpec instanceof GOST3410PublicKeySpec)
++    //         {
++    //             return new JDKGOST3410PublicKey((GOST3410PublicKeySpec)keySpec);
++    //         }
++    //
++    //         return super.engineGeneratePublic(keySpec);
++    //     }
++    // }
+     
+-        protected PublicKey engineGeneratePublic(
+-            KeySpec    keySpec)
+-            throws InvalidKeySpecException
+-        {
+-            if (keySpec instanceof ElGamalPublicKeySpec)
+-            {
+-                return new JCEElGamalPublicKey((ElGamalPublicKeySpec)keySpec);
+-            }
+-            else if (keySpec instanceof DHPublicKeySpec)
+-            {
+-                return new JCEElGamalPublicKey((DHPublicKeySpec)keySpec);
+-            }
+-
+-            return super.engineGeneratePublic(keySpec);
+-        }
+-    }
+-
+-
+-    /**
+-     * This isn't really correct, however the class path project API seems to think such
+-     * a key factory will exist.
+-     */
+-    public static class X509
+-        extends JDKKeyFactory
+-    {
+-        public X509()
+-        {
+-        }
+-    }
++    // public static class ElGamal
++    //     extends JDKKeyFactory
++    // {
++    //     public ElGamal()
++    //     {
++    //         elGamalFactory = true;
++    //     }
++    //
++    //     protected PrivateKey engineGeneratePrivate(
++    //         KeySpec    keySpec)
++    //         throws InvalidKeySpecException
++    //     {
++    //         if (keySpec instanceof ElGamalPrivateKeySpec)
++    //         {
++    //             return new JCEElGamalPrivateKey((ElGamalPrivateKeySpec)keySpec);
++    //         }
++    //         else if (keySpec instanceof DHPrivateKeySpec)
++    //         {
++    //             return new JCEElGamalPrivateKey((DHPrivateKeySpec)keySpec);
++    //         }
++    //
++    //         return super.engineGeneratePrivate(keySpec);
++    //     }
++    //
++    //     protected PublicKey engineGeneratePublic(
++    //         KeySpec    keySpec)
++    //         throws InvalidKeySpecException
++    //     {
++    //         if (keySpec instanceof ElGamalPublicKeySpec)
++    //         {
++    //             return new JCEElGamalPublicKey((ElGamalPublicKeySpec)keySpec);
++    //         }
++    //         else if (keySpec instanceof DHPublicKeySpec)
++    //         {
++    //             return new JCEElGamalPublicKey((DHPublicKeySpec)keySpec);
++    //         }
++    //
++    //         return super.engineGeneratePublic(keySpec);
++    //     }
++    // }
++    //
++    //
++    //
++    // /**
++    //  * This isn't really correct, however the class path project API seems to think such
++    //  * a key factory will exist.
++    //  */
++    // public static class X509
++    //     extends JDKKeyFactory
++    // {
++    //     public X509()
++    //     {
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java	2010-06-25 23:17:26.000000000 +0000
+@@ -6,9 +6,11 @@
+ import org.bouncycastle.crypto.generators.DHParametersGenerator;
+ import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
+ import org.bouncycastle.crypto.generators.DSAParametersGenerator;
+-import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
+-import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
+-import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
++// import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
++// import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
++// END android-removed
+ import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+ import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+ import org.bouncycastle.crypto.params.DHParameters;
+@@ -18,20 +20,24 @@
+ import org.bouncycastle.crypto.params.DSAParameters;
+ import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+ import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+-import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
+-import org.bouncycastle.crypto.params.ElGamalParameters;
+-import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+-import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+-import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
+-import org.bouncycastle.crypto.params.GOST3410Parameters;
+-import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
+-import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
++// import org.bouncycastle.crypto.params.ElGamalParameters;
++// import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
++// import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
++// import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
++// import org.bouncycastle.crypto.params.GOST3410Parameters;
++// import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
++// import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
++// END android-removed
+ import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+ import org.bouncycastle.crypto.params.RSAKeyParameters;
+ import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+-import org.bouncycastle.jce.spec.ElGamalParameterSpec;
+-import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+-import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
++// BEGIN android-removed
++// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
++// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
++// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
++// END android-removed
+ 
+ import java.math.BigInteger;
+ import java.security.InvalidAlgorithmParameterException;
+@@ -163,7 +169,9 @@
+         {
+             if (!initialised)
+             {
+-                Integer paramStrength = new Integer(strength);
++                // BEGIN android-changed
++                Integer paramStrength = Integer.valueOf(strength);
++                // END android-changed
+ 
+                 if (params.containsKey(paramStrength))
+                 {
+@@ -260,139 +268,143 @@
+         }
+     }
+ 
+-    public static class ElGamal
+-        extends JDKKeyPairGenerator
+-    {
+-        ElGamalKeyGenerationParameters  param;
+-        ElGamalKeyPairGenerator         engine = new ElGamalKeyPairGenerator();
+-        int                             strength = 1024;
+-        int                             certainty = 20;
+-        SecureRandom                    random = new SecureRandom();
+-        boolean                         initialised = false;
+-
+-        public ElGamal()
+-        {
+-            super("ElGamal");
+-        }
+-
+-        public void initialize(
+-            int             strength,
+-            SecureRandom    random)
+-        {
+-            this.strength = strength;
+-            this.random = random;
+-        }
+-
+-        public void initialize(
+-            AlgorithmParameterSpec  params,
+-            SecureRandom            random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            if (!(params instanceof ElGamalParameterSpec) && !(params instanceof DHParameterSpec))
+-            {
+-                throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec or an ElGamalParameterSpec");
+-            }
+-            
+-            if (params instanceof ElGamalParameterSpec)
+-            {
+-                ElGamalParameterSpec     elParams = (ElGamalParameterSpec)params;
+-
+-                param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(elParams.getP(), elParams.getG()));
+-            }
+-            else
+-            {
+-                DHParameterSpec     dhParams = (DHParameterSpec)params;
+-
+-                param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG(), dhParams.getL()));
+-            }
+-
+-            engine.init(param);
+-            initialised = true;
+-        }
+-
+-        public KeyPair generateKeyPair()
+-        {
+-            if (!initialised)
+-            {
+-                ElGamalParametersGenerator   pGen = new ElGamalParametersGenerator();
+-
+-                pGen.init(strength, certainty, random);
+-                param = new ElGamalKeyGenerationParameters(random, pGen.generateParameters());
+-                engine.init(param);
+-                initialised = true;
+-            }
+-
+-            AsymmetricCipherKeyPair         pair = engine.generateKeyPair();
+-            ElGamalPublicKeyParameters      pub = (ElGamalPublicKeyParameters)pair.getPublic();
+-            ElGamalPrivateKeyParameters     priv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+-
+-            return new KeyPair(new JCEElGamalPublicKey(pub),
+-                               new JCEElGamalPrivateKey(priv));
+-        }
+-    }
+-
+-    public static class GOST3410
+-        extends JDKKeyPairGenerator
+-    {
+-        GOST3410KeyGenerationParameters param;
+-        GOST3410KeyPairGenerator        engine = new GOST3410KeyPairGenerator();
+-        GOST3410ParameterSpec           gost3410Params;
+-        int                             strength = 1024;
+-        SecureRandom                    random = null;
+-        boolean                         initialised = false;
+-
+-        public GOST3410()
+-        {
+-            super("GOST3410");
+-        }
+-
+-        public void initialize(
+-            int             strength,
+-            SecureRandom    random)
+-        {
+-            this.strength = strength;
+-            this.random = random;
+-        }
+-    
+-        private void init(
+-            GOST3410ParameterSpec gParams,
+-            SecureRandom          random)
+-        {
+-            GOST3410PublicKeyParameterSetSpec spec = gParams.getPublicKeyParameters();
+-            
+-            param = new GOST3410KeyGenerationParameters(random, new GOST3410Parameters(spec.getP(), spec.getQ(), spec.getA()));
+-            
+-            engine.init(param);
+-            
+-            initialised = true;
+-            gost3410Params = gParams;
+-        }
+-        
+-        public void initialize(
+-            AlgorithmParameterSpec  params,
+-            SecureRandom            random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            if (!(params instanceof GOST3410ParameterSpec))
+-            {
+-                throw new InvalidAlgorithmParameterException("parameter object not a GOST3410ParameterSpec");
+-            }
+-            
+-            init((GOST3410ParameterSpec)params, random);
+-        }
+-
+-        public KeyPair generateKeyPair()
+-        {
+-            if (!initialised)
+-            {
+-                init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom());
+-            }
+-            
+-            AsymmetricCipherKeyPair   pair = engine.generateKeyPair();
+-            GOST3410PublicKeyParameters  pub = (GOST3410PublicKeyParameters)pair.getPublic();
+-            GOST3410PrivateKeyParameters priv = (GOST3410PrivateKeyParameters)pair.getPrivate();
+-            
+-            return new KeyPair(new JDKGOST3410PublicKey(pub, gost3410Params), new JDKGOST3410PrivateKey(priv, gost3410Params));
+-        }
+-   }
++    // BEGIN android-removed
++    // public static class ElGamal
++    //     extends JDKKeyPairGenerator
++    // {
++    //     ElGamalKeyGenerationParameters  param;
++    //     ElGamalKeyPairGenerator         engine = new ElGamalKeyPairGenerator();
++    //     int                             strength = 1024;
++    //     int                             certainty = 20;
++    //     SecureRandom                    random = new SecureRandom();
++    //     boolean                         initialised = false;
++    //
++    //     public ElGamal()
++    //     {
++    //         super("ElGamal");
++    //     }
++    //
++    //     public void initialize(
++    //         int             strength,
++    //         SecureRandom    random)
++    //     {
++    //         this.strength = strength;
++    //         this.random = random;
++    //     }
++    //
++    //     public void initialize(
++    //         AlgorithmParameterSpec  params,
++    //         SecureRandom            random)
++    //         throws InvalidAlgorithmParameterException
++    //     {
++    //         if (!(params instanceof ElGamalParameterSpec) && !(params instanceof DHParameterSpec))
++    //         {
++    //             throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec or an ElGamalParameterSpec");
++    //         }
++    //
++    //         if (params instanceof ElGamalParameterSpec)
++    //         {
++    //             ElGamalParameterSpec     elParams = (ElGamalParameterSpec)params;
++
++    //             param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(elParams.getP(), elParams.getG()));
++    //         }
++    //         else
++    //         {
++    //             DHParameterSpec     dhParams = (DHParameterSpec)params;
++    //
++    //             param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG(), dhParams.getL()));
++    //         }
++    //
++    //         engine.init(param);
++    //         initialised = true;
++    //     }
++    //
++    //     public KeyPair generateKeyPair()
++    //     {
++    //         if (!initialised)
++    //         {
++    //             ElGamalParametersGenerator   pGen = new ElGamalParametersGenerator();
++    //
++    //             pGen.init(strength, certainty, random);
++    //             param = new ElGamalKeyGenerationParameters(random, pGen.generateParameters());
++    //             engine.init(param);
++    //             initialised = true;
++    //         }
++    //
++    //         AsymmetricCipherKeyPair         pair = engine.generateKeyPair();
++    //         ElGamalPublicKeyParameters      pub = (ElGamalPublicKeyParameters)pair.getPublic();
++    //         ElGamalPrivateKeyParameters     priv = (ElGamalPrivateKeyParameters)pair.getPrivate();
++    //
++    //         return new KeyPair(new JCEElGamalPublicKey(pub),
++    //                            new JCEElGamalPrivateKey(priv));
++    //     }
++    // }
++    // END android-removed
++
++   // BEGIN android-removed
++   //  public static class GOST3410
++   //      extends JDKKeyPairGenerator
++   //  {
++   //      GOST3410KeyGenerationParameters param;
++   //      GOST3410KeyPairGenerator        engine = new GOST3410KeyPairGenerator();
++   //      GOST3410ParameterSpec           gost3410Params;
++   //      int                             strength = 1024;
++   //      SecureRandom                    random = null;
++   //      boolean                         initialised = false;
++   //
++   //      public GOST3410()
++   //      {
++   //          super("GOST3410");
++   //      }
++   //
++   //      public void initialize(
++   //          int             strength,
++   //          SecureRandom    random)
++   //      {
++   //          this.strength = strength;
++   //          this.random = random;
++   //      }
++   //
++   //      private void init(
++   //          GOST3410ParameterSpec gParams,
++   //          SecureRandom          random)
++   //      {
++   //          GOST3410PublicKeyParameterSetSpec spec = gParams.getPublicKeyParameters();
++   //
++   //          param = new GOST3410KeyGenerationParameters(random, new GOST3410Parameters(spec.getP(), spec.getQ(), spec.getA()));
++   //
++   //          engine.init(param);
++   //
++   //          initialised = true;
++   //          gost3410Params = gParams;
++   //      }
++   //
++   //      public void initialize(
++   //          AlgorithmParameterSpec  params,
++   //          SecureRandom            random)
++   //          throws InvalidAlgorithmParameterException
++   //      {
++   //          if (!(params instanceof GOST3410ParameterSpec))
++   //          {
++   //              throw new InvalidAlgorithmParameterException("parameter object not a GOST3410ParameterSpec");
++   //          }
++   //
++   //          init((GOST3410ParameterSpec)params, random);
++   //      }
++   //
++   //      public KeyPair generateKeyPair()
++   //      {
++   //          if (!initialised)
++   //          {
++   //              init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom());
++   //          }
++   //         
++   //          AsymmetricCipherKeyPair   pair = engine.generateKeyPair();
++   //          GOST3410PublicKeyParameters  pub = (GOST3410PublicKeyParameters)pair.getPublic();
++   //          GOST3410PrivateKeyParameters priv = (GOST3410PrivateKeyParameters)pair.getPrivate();
++   //
++   //          return new KeyPair(new JDKGOST3410PublicKey(pub, gost3410Params), new JDKGOST3410PrivateKey(priv, gost3410Params));
++   //      }
++   // }
++   // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyStore.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyStore.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKKeyStore.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKKeyStore.java	2010-06-25 23:17:26.000000000 +0000
+@@ -39,7 +39,12 @@
+ import org.bouncycastle.crypto.CipherParameters;
+ import org.bouncycastle.crypto.Digest;
+ import org.bouncycastle.crypto.PBEParametersGenerator;
+-import org.bouncycastle.crypto.digests.SHA1Digest;
++// BEGIN android-added
++import org.bouncycastle.crypto.digests.OpenSSLDigest;
++// END android-added
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.SHA1Digest;
++// END android-removed
+ import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
+ import org.bouncycastle.crypto.io.DigestInputStream;
+ import org.bouncycastle.crypto.io.DigestOutputStream;
+@@ -810,12 +815,16 @@
+         //
+         // we only do an integrity check if the password is provided.
+         //
+-        HMac hMac = new HMac(new SHA1Digest());
++        // BEGIN android-changed
++        HMac hMac = new HMac(new OpenSSLDigest.SHA1());
++        // END android-changed
+         if (password != null && password.length != 0)
+         {
+             byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
+ 
+-            PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
++            // BEGIN android-changed
++            PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(new OpenSSLDigest.SHA1());
++            // END android-changed
+             pbeGen.init(passKey, salt, iterationCount);
+             CipherParameters macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize());
+             Arrays.fill(passKey, (byte)0);
+@@ -866,9 +875,11 @@
+         dOut.write(salt);
+         dOut.writeInt(iterationCount);
+ 
+-        HMac                    hMac = new HMac(new SHA1Digest());
++        // BEGIN android-changed
++        HMac                    hMac = new HMac(new OpenSSLDigest.SHA1());
+         MacOutputStream         mOut = new MacOutputStream(dOut, hMac);
+-        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
++        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new OpenSSLDigest.SHA1());
++        // END android-changed
+         byte[]                  passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
+ 
+         pbeGen.init(passKey, salt, iterationCount);
+@@ -956,7 +967,9 @@
+             Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+             CipherInputStream cIn = new CipherInputStream(dIn, cipher);
+ 
+-            Digest dig = new SHA1Digest();
++            // BEGIN android-changed
++            Digest dig = new OpenSSLDigest.SHA1();
++            // END android-changed
+             DigestInputStream  dgIn = new DigestInputStream(cIn, dig);
+     
+             this.loadStore(dgIn);
+@@ -996,8 +1009,9 @@
+             cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
+     
+             CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
+-            DigestOutputStream  dgOut = new DigestOutputStream(cOut, new SHA1Digest());
+-    
++            // BEGIN android-changed
++            DigestOutputStream  dgOut = new DigestOutputStream(cOut, new OpenSSLDigest.SHA1());
++            // END android-changed
+             this.saveStore(dgOut);
+     
+             Digest  dig = dgOut.getDigest();
+@@ -1009,5 +1023,5 @@
+     
+             cOut.close();
+         }
+-    }
++    }    
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKMessageDigest.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKMessageDigest.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKMessageDigest.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKMessageDigest.java	2010-06-25 23:17:26.000000000 +0000
+@@ -57,36 +57,38 @@
+         {
+             super(new SHA1Digest());
+         }
+-
++    
+         public Object clone()
+             throws CloneNotSupportedException
+         {
+             SHA1 d = (SHA1)super.clone();
+             d.digest = new SHA1Digest((SHA1Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-
+-    static public class SHA224
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public SHA224()
+-        {
+-            super(new SHA224Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            SHA224 d = (SHA224)super.clone();
+-            d.digest = new SHA224Digest((SHA224Digest)digest);
+-
++    
+             return d;
+         }
+     }
+-
++    
++    // BEGIN android-removed
++    // static public class SHA224
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public SHA224()
++    //     {
++    //         super(new SHA224Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         SHA224 d = (SHA224)super.clone();
++    //         d.digest = new SHA224Digest((SHA224Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    // END android-removed
++    
+     static public class SHA256
+         extends JDKMessageDigest
+         implements Cloneable
+@@ -95,13 +97,13 @@
+         {
+             super(new SHA256Digest());
+         }
+-
++    
+         public Object clone()
+             throws CloneNotSupportedException
+         {
+             SHA256 d = (SHA256)super.clone();
+             d.digest = new SHA256Digest((SHA256Digest)digest);
+-
++    
+             return d;
+         }
+     }
+@@ -144,43 +146,45 @@
+         }
+     }
+ 
+-    static public class MD2
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public MD2()
+-        {
+-            super(new MD2Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            MD2 d = (MD2)super.clone();
+-            d.digest = new MD2Digest((MD2Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-
+-    static public class MD4
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public MD4()
+-        {
+-            super(new MD4Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            MD4 d = (MD4)super.clone();
+-            d.digest = new MD4Digest((MD4Digest)digest);
+-
+-            return d;
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class MD2
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public MD2()
++    //     {
++    //         super(new MD2Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         MD2 d = (MD2)super.clone();
++    //         d.digest = new MD2Digest((MD2Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //
++    // static public class MD4
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public MD4()
++    //     {
++    //         super(new MD4Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         MD4 d = (MD4)super.clone();
++    //         d.digest = new MD4Digest((MD4Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    // END android-removed
+ 
+     static public class MD5
+         extends JDKMessageDigest
+@@ -190,147 +194,149 @@
+         {
+             super(new MD5Digest());
+         }
+-
++   
+         public Object clone()
+             throws CloneNotSupportedException
+         {
+             MD5 d = (MD5)super.clone();
+             d.digest = new MD5Digest((MD5Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-
+-    static public class RIPEMD128
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public RIPEMD128()
+-        {
+-            super(new RIPEMD128Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            RIPEMD128 d = (RIPEMD128)super.clone();
+-            d.digest = new RIPEMD128Digest((RIPEMD128Digest)digest);
+-
++   
+             return d;
+         }
+     }
+ 
+-    static public class RIPEMD160
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public RIPEMD160()
+-        {
+-            super(new RIPEMD160Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            RIPEMD160 d = (RIPEMD160)super.clone();
+-            d.digest = new RIPEMD160Digest((RIPEMD160Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-    
+-    static public class RIPEMD256
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public RIPEMD256()
+-        {
+-            super(new RIPEMD256Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            RIPEMD256 d = (RIPEMD256)super.clone();
+-            d.digest = new RIPEMD256Digest((RIPEMD256Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-    
+-    static public class RIPEMD320
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public RIPEMD320()
+-        {
+-            super(new RIPEMD320Digest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            RIPEMD320 d = (RIPEMD320)super.clone();
+-            d.digest = new RIPEMD320Digest((RIPEMD320Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-    
+-    static public class Tiger
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public Tiger()
+-        {
+-            super(new TigerDigest());
+-        }
+-
+-        public Object clone()
+-            throws CloneNotSupportedException
+-        {
+-            Tiger d = (Tiger)super.clone();
+-            d.digest = new TigerDigest((TigerDigest)digest);
+-
+-            return d;
+-        }
+-    }
+-    
+-    static public class GOST3411
+-        extends JDKMessageDigest
+-        implements Cloneable
+-    {
+-        public GOST3411()
+-        {
+-            super(new GOST3411Digest());
+-        }
+-    
+-        public Object clone()
+-        throws CloneNotSupportedException
+-        {
+-            GOST3411 d = (GOST3411)super.clone();
+-            d.digest = new GOST3411Digest((GOST3411Digest)digest);
+-
+-            return d;
+-        }
+-    }
+-    
+-    static public class Whirlpool
+-       extends JDKMessageDigest
+-       implements Cloneable
+-    {
+-        public Whirlpool()
+-        {
+-            super(new WhirlpoolDigest());
+-        }
+-        
+-        public Object clone()
+-        throws CloneNotSupportedException
+-        {
+-            Whirlpool d = (Whirlpool)super.clone();
+-            d.digest = new WhirlpoolDigest((WhirlpoolDigest)digest);
+-            
+-            return d;
+-        }
+-    }
++    // BEGIN android-removed
++    // static public class RIPEMD128
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public RIPEMD128()
++    //     {
++    //         super(new RIPEMD128Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         RIPEMD128 d = (RIPEMD128)super.clone();
++    //         d.digest = new RIPEMD128Digest((RIPEMD128Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //
++    // static public class RIPEMD160
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public RIPEMD160()
++    //     {
++    //         super(new RIPEMD160Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         RIPEMD160 d = (RIPEMD160)super.clone();
++    //         d.digest = new RIPEMD160Digest((RIPEMD160Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //   
++    // static public class RIPEMD256
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public RIPEMD256()
++    //     {
++    //         super(new RIPEMD256Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         RIPEMD256 d = (RIPEMD256)super.clone();
++    //         d.digest = new RIPEMD256Digest((RIPEMD256Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //   
++    // static public class RIPEMD320
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public RIPEMD320()
++    //     {
++    //         super(new RIPEMD320Digest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         RIPEMD320 d = (RIPEMD320)super.clone();
++    //         d.digest = new RIPEMD320Digest((RIPEMD320Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //   
++    // static public class Tiger
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public Tiger()
++    //     {
++    //         super(new TigerDigest());
++    //     }
++    //
++    //     public Object clone()
++    //         throws CloneNotSupportedException
++    //     {
++    //         Tiger d = (Tiger)super.clone();
++    //         d.digest = new TigerDigest((TigerDigest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //   
++    // static public class GOST3411
++    //     extends JDKMessageDigest
++    //     implements Cloneable
++    // {
++    //     public GOST3411()
++    //     {
++    //         super(new GOST3411Digest());
++    //     }
++    //   
++    //     public Object clone()
++    //     throws CloneNotSupportedException
++    //     {
++    //         GOST3411 d = (GOST3411)super.clone();
++    //         d.digest = new GOST3411Digest((GOST3411Digest)digest);
++    //
++    //         return d;
++    //     }
++    // }
++    //   
++    // static public class Whirlpool
++    //    extends JDKMessageDigest
++    //    implements Cloneable
++    // {
++    //     public Whirlpool()
++    //     {
++    //         super(new WhirlpoolDigest());
++    //     }
++    //       
++    //     public Object clone()
++    //     throws CloneNotSupportedException
++    //     {
++    //         Whirlpool d = (Whirlpool)super.clone();
++    //         d.digest = new WhirlpoolDigest((WhirlpoolDigest)digest);
++    //           
++    //         return d;
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java	2010-06-25 23:17:26.000000000 +0000
+@@ -1434,7 +1434,9 @@
+         {
+             byte[] res = calculatePbeMac(id_SHA1, mSalt, itCount, password, false, data);
+ 
+-            AlgorithmIdentifier     algId = new AlgorithmIdentifier(id_SHA1, new DERNull());
++            // BEGIN android-changed
++            AlgorithmIdentifier     algId = new AlgorithmIdentifier(id_SHA1, DERNull.INSTANCE);
++            // END android-changed
+             DigestInfo              dInfo = new DigestInfo(algId, res);
+ 
+             mData = new MacData(dInfo, mSalt, itCount);
+@@ -1484,32 +1486,34 @@
+         }
+     }
+ 
+-    public static class BCPKCS12KeyStore3DES
+-        extends JDKPKCS12KeyStore
+-    {
+-        public BCPKCS12KeyStore3DES()
+-        {
+-            super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+-        }
+-    }
+-
+-    public static class DefPKCS12KeyStore
+-        extends JDKPKCS12KeyStore
+-    {
+-        public DefPKCS12KeyStore()
+-        {
+-            super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbewithSHAAnd40BitRC2_CBC);
+-        }
+-    }
+-
+-    public static class DefPKCS12KeyStore3DES
+-        extends JDKPKCS12KeyStore
+-    {
+-        public DefPKCS12KeyStore3DES()
+-        {
+-            super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class BCPKCS12KeyStore3DES
++    //     extends JDKPKCS12KeyStore
++    // {
++    //     public BCPKCS12KeyStore3DES()
++    //     {
++    //         super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
++    //     }
++    // }
++    //
++    // public static class DefPKCS12KeyStore
++    //     extends JDKPKCS12KeyStore
++    // {
++    //     public DefPKCS12KeyStore()
++    //     {
++    //         super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbewithSHAAnd40BitRC2_CBC);
++    //     }
++    // }
++    //
++    // public static class DefPKCS12KeyStore3DES
++    //     extends JDKPKCS12KeyStore
++    // {
++    //     public DefPKCS12KeyStore3DES()
++    //     {
++    //         super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
++    //     }
++    // }
++    // END android-removed
+ 
+     private static class IgnoresCaseHashtable
+     {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PBE.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PBE.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PBE.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PBE.java	2010-06-25 23:17:26.000000000 +0000
+@@ -7,12 +7,18 @@
+ 
+ import org.bouncycastle.crypto.CipherParameters;
+ import org.bouncycastle.crypto.PBEParametersGenerator;
+-import org.bouncycastle.crypto.digests.MD2Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.MD2Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.MD5Digest;
+-import org.bouncycastle.crypto.digests.RIPEMD160Digest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
++// END android-removed
+ import org.bouncycastle.crypto.digests.SHA1Digest;
+ import org.bouncycastle.crypto.digests.SHA256Digest;
+-import org.bouncycastle.crypto.digests.TigerDigest;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.digests.TigerDigest;
++// END android-removed
+ import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
+ import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
+ import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
+@@ -53,9 +59,11 @@
+             {
+                 switch (hash)
+                 {
+-                case MD2:
+-                    generator = new PKCS5S1ParametersGenerator(new MD2Digest());
+-                    break;
++                // BEGIN android-removed
++                // case MD2:
++                //     generator = new PKCS5S1ParametersGenerator(new MD2Digest());
++                //     break;
++                // END android-removed
+                 case MD5:
+                     generator = new PKCS5S1ParametersGenerator(new MD5Digest());
+                     break;
+@@ -74,21 +82,25 @@
+             {
+                 switch (hash)
+                 {
+-                case MD2:
+-                    generator = new PKCS12ParametersGenerator(new MD2Digest());
+-                    break;
++                // BEGIN android-removed
++                // case MD2:
++                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
++                //     break;
++                // END android-removed
+                 case MD5:
+                     generator = new PKCS12ParametersGenerator(new MD5Digest());
+                     break;
+                 case SHA1:
+                     generator = new PKCS12ParametersGenerator(new SHA1Digest());
+                     break;
+-                case RIPEMD160:
+-                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
+-                    break;
+-                case TIGER:
+-                    generator = new PKCS12ParametersGenerator(new TigerDigest());
+-                    break;
++                // BEGIN android-removed
++                // case RIPEMD160:
++                //     generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
++                //     break;
++                // case TIGER:
++                //     generator = new PKCS12ParametersGenerator(new TigerDigest());
++                //     break;
++                // END android-removed
+                 case SHA256:
+                     generator = new PKCS12ParametersGenerator(new SHA256Digest());
+                     break;
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java	2010-06-25 23:17:26.000000000 +0000
+@@ -1,6 +1,9 @@
+ package org.bouncycastle.jce.provider;
+ 
+ import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
++// BEGIN android-added
++import org.bouncycastle.asn1.OrderedTable;
++// END android-added
+ import org.bouncycastle.asn1.DERObjectIdentifier;
+ import org.bouncycastle.asn1.DEREncodable;
+ import org.bouncycastle.asn1.ASN1OutputStream;
+@@ -17,65 +20,73 @@
+ class PKCS12BagAttributeCarrierImpl
+     implements PKCS12BagAttributeCarrier
+ {
+-    private Hashtable pkcs12Attributes;
+-    private Vector pkcs12Ordering;
+-
+-    PKCS12BagAttributeCarrierImpl(Hashtable attributes, Vector ordering)
+-    {
+-        this.pkcs12Attributes = attributes;
+-        this.pkcs12Ordering = ordering;
+-    }
++    // BEGIN android-changed
++    private OrderedTable pkcs12 = new OrderedTable();
++    // END android-changed
++
++    // BEGIN android-removed
++    // PKCS12BagAttributeCarrierImpl(Hashtable attributes, Vector ordering)
++    // {
++    //     this.pkcs12Attributes = attributes;
++    //     this.pkcs12Ordering = ordering;
++    // }
++    // END android-removed
+ 
+     public PKCS12BagAttributeCarrierImpl()
+     {
+-        this(new Hashtable(), new Vector());
++        // BEGIN android-removed
++        // this(new Hashtable(), new Vector());
++        // END android-removed
+     }
+ 
+     public void setBagAttribute(
+         DERObjectIdentifier oid,
+         DEREncodable        attribute)
+     {
+-        if (pkcs12Attributes.containsKey(oid))
+-        {                           // preserve original ordering
+-            pkcs12Attributes.put(oid, attribute);
+-        }
+-        else
+-        {
+-            pkcs12Attributes.put(oid, attribute);
+-            pkcs12Ordering.addElement(oid);
+-        }
++        // BEGIN android-changed
++        // preserve original ordering
++        pkcs12.put(oid, attribute);
++        // END android-changed
+     }
+ 
+     public DEREncodable getBagAttribute(
+         DERObjectIdentifier oid)
+     {
+-        return (DEREncodable)pkcs12Attributes.get(oid);
++        // BEGIN android-changed
++        return (DEREncodable)pkcs12.get(oid);
++        // END android-changed
+     }
+ 
+     public Enumeration getBagAttributeKeys()
+     {
+-        return pkcs12Ordering.elements();
++        // BEGIN android-changed
++        return pkcs12.getKeys();
++        // END android-changed
+     }
+ 
+     int size()
+     {
+-        return pkcs12Ordering.size();
+-    }
+-
+-    Hashtable getAttributes()
+-    {
+-        return pkcs12Attributes;
+-    }
+-
+-    Vector getOrdering()
+-    {
+-        return pkcs12Ordering;
+-    }
++        // BEGIN android-changed
++        return pkcs12.size();
++        // END android-changed
++    }
++
++    // BEGIN android-removed
++    // Hashtable getAttributes()
++    // {
++    //     return pkcs12Attributes;
++    // }
++    //
++    // Vector getOrdering()
++    // {
++    //     return pkcs12Ordering;
++    // }
++    // END android-removed
+ 
+     public void writeObject(ObjectOutputStream out)
+         throws IOException
+     {
+-        if (pkcs12Ordering.size() == 0)
++        if (pkcs12.size() == 0)
+         {
+             out.writeObject(new Hashtable());
+             out.writeObject(new Vector());
+@@ -92,7 +103,7 @@
+                 DERObjectIdentifier    oid = (DERObjectIdentifier)e.nextElement();
+ 
+                 aOut.writeObject(oid);
+-                aOut.writeObject(pkcs12Attributes.get(oid));
++                aOut.writeObject(pkcs12.get(oid));
+             }
+ 
+             out.writeObject(bOut.toByteArray());
+@@ -106,8 +117,11 @@
+ 
+         if (obj instanceof Hashtable)
+         {
+-            this.pkcs12Attributes = (Hashtable)obj;
+-            this.pkcs12Ordering = (Vector)in.readObject();
++            // BEGIN android-changed
++            // we only write out Hashtable/Vector in empty case
++            in.readObject(); // consume empty Vector
++            this.pkcs12 = new OrderedTable();
++            // END android-changed
+         }
+         else
+         {
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPath.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPath.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPath.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPath.java	2010-06-25 23:17:26.000000000 +0000
+@@ -33,7 +33,9 @@
+ import org.bouncycastle.asn1.pkcs.ContentInfo;
+ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+ import org.bouncycastle.asn1.pkcs.SignedData;
+-import org.bouncycastle.openssl.PEMWriter;
++// BEGIN android-removed
++// import org.bouncycastle.openssl.PEMWriter;
++// END android-removed
+ 
+ /**
+  * CertPath implementation for X.509 certificates.
+@@ -295,27 +297,29 @@
+             return toDEREncoded(new ContentInfo(
+                     PKCSObjectIdentifiers.signedData, sd));
+         }
+-        else if (encoding.equalsIgnoreCase("PEM"))
+-        {
+-            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+-            PEMWriter             pWrt = new PEMWriter(new OutputStreamWriter(bOut));
+-
+-            try
+-            {
+-                for (int i = 0; i != certificates.size(); i++)
+-                {
+-                    pWrt.writeObject(certificates.get(i));
+-                }
+-            
+-                pWrt.close();
+-            }
+-            catch (Exception e)
+-            {
+-                throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
+-            }
+-
+-            return bOut.toByteArray();
+-        }
++        // BEGIN android-removed
++        // else if (encoding.equalsIgnoreCase("PEM"))
++        // {
++        //     ByteArrayOutputStream bOut = new ByteArrayOutputStream();
++        //     PEMWriter             pWrt = new PEMWriter(new OutputStreamWriter(bOut));
++        //
++        //     try
++        //     {
++        //         for (int i = 0; i != certificates.size(); i++)
++        //         {
++        //             pWrt.writeObject(certificates.get(i));
++        //         }
++        //     
++        //         pWrt.close();
++        //     }
++        //     catch (Exception e)
++        //     {
++        //         throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
++        //     }
++        //
++        //     return bOut.toByteArray();
++        // }
++        // END android-removed
+         else
+         {
+             throw new CertificateEncodingException("unsupported encoding: " + encoding);
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java	2010-06-25 23:17:26.000000000 +0000
+@@ -172,8 +172,9 @@
+         try
+         {
+             // check whether the issuer of <tbvCert> is a TrustAnchor
+-            if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getTrustAnchors(),
+-                pkixParams.getSigProvider()) != null)
++            // BEGIN android-changed
++            if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams) != null)
++                // END android-changed
+             {
+                 // exception message from possibly later tried certification
+                 // chains
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java	2010-06-25 23:17:26.000000000 +0000
+@@ -13,6 +13,7 @@
+ import java.security.cert.TrustAnchor;
+ import java.security.cert.X509Certificate;
+ import java.util.ArrayList;
++import java.util.Arrays;
+ import java.util.HashSet;
+ import java.util.Iterator;
+ import java.util.List;
+@@ -90,10 +91,14 @@
+         // (d)
+         // 
+         TrustAnchor trust;
++        // BEGIN android-added
++        X509Certificate lastCert = (X509Certificate) certs.get(certs.size() - 1);
++        // END android-added
+         try
+         {
+-            trust = CertPathValidatorUtilities.findTrustAnchor((X509Certificate) certs.get(certs.size() - 1),
+-                    paramsPKIX.getTrustAnchors(), paramsPKIX.getSigProvider());
++            // BEGIN android-changed
++            trust = CertPathValidatorUtilities.findTrustAnchor(lastCert, paramsPKIX);
++            // END android-changed
+         }
+         catch (AnnotatedException e)
+         {
+@@ -189,12 +194,25 @@
+         X500Principal workingIssuerName;
+ 
+         X509Certificate sign = trust.getTrustedCert();
++        // BEGIN android-added
++        boolean trustAnchorInChain = false;
++        // END android-added
+         try
+         {
+             if (sign != null)
+             {
+                 workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
+                 workingPublicKey = sign.getPublicKey();
++                // BEGIN android-added
++                // There is similar code in CertPathValidatorUtilities.
++                try {
++                    byte[] trustBytes = sign.getEncoded();
++                    byte[] certBytes = lastCert.getEncoded();
++                    trustAnchorInChain = Arrays.equals(trustBytes, certBytes);
++                } catch(Exception e) {
++                    // ignore, continue with trustAnchorInChain being false
++                }
++                // END android-added
+             }
+             else
+             {
+@@ -271,8 +289,10 @@
+             // 6.1.3
+             //
+ 
++            // BEGIN android-changed
+             RFC3280CertPathUtilities.processCertA(certPath, paramsPKIX, index, workingPublicKey,
+-                verificationAlreadyPerformed, workingIssuerName, sign);
++                verificationAlreadyPerformed, workingIssuerName, sign, i, trustAnchorInChain);
++            // END android-changed
+ 
+             RFC3280CertPathUtilities.processCertBC(certPath, index, nameConstraintValidator);
+ 
+@@ -289,11 +309,18 @@
+ 
+             if (i != n)
+             {
++                // BEGIN android-added
++                if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
++                {
++                // END android-added
+                 if (cert != null && cert.getVersion() == 1)
+                 {
+                     throw new CertPathValidatorException("Version 1 certificates can't be used as CA ones.", null,
+                             certPath, index);
+                 }
++                // BEGIN android-added
++                }
++                // END android-added
+ 
+                 RFC3280CertPathUtilities.prepareNextCertA(certPath, index);
+ 
+@@ -317,7 +344,9 @@
+                 inhibitAnyPolicy = RFC3280CertPathUtilities.prepareNextCertJ(certPath, index, inhibitAnyPolicy);
+ 
+                 // (k)
+-                RFC3280CertPathUtilities.prepareNextCertK(certPath, index);
++                // BEGIN android-changed
++                RFC3280CertPathUtilities.prepareNextCertK(certPath, index, i, trustAnchorInChain);
++                // END android-changed
+ 
+                 // (l)
+                 maxPathLength = RFC3280CertPathUtilities.prepareNextCertL(certPath, index, maxPathLength);
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java	2010-06-25 23:17:26.000000000 +0000
+@@ -1533,7 +1533,9 @@
+         for (Enumeration e = permitted.getObjects(); e.hasMoreElements();)
+         {
+             GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement());
+-            Integer tagNo = new Integer(subtree.getBase().getTagNo());
++            // BEGIN android-changed
++            Integer tagNo = Integer.valueOf(subtree.getBase().getTagNo());
++            // END android-changed
+             if (subtreesMap.get(tagNo) == null)
+             {
+                 subtreesMap.put(tagNo, new HashSet());
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/ProviderUtil.java bcprov-jdk16-145/org/bouncycastle/jce/provider/ProviderUtil.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/ProviderUtil.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/ProviderUtil.java	2010-06-25 23:17:26.000000000 +0000
+@@ -1,9 +1,13 @@
+ package org.bouncycastle.jce.provider;
+ 
+ import org.bouncycastle.jce.ProviderConfigurationPermission;
+-import org.bouncycastle.jce.provider.asymmetric.ec.EC5Util;
++// BEGIN android-removed
++// import org.bouncycastle.jce.provider.asymmetric.ec.EC5Util;
++// END android-removed
+ import org.bouncycastle.jce.interfaces.ConfigurableProvider;
+-import org.bouncycastle.jce.spec.ECParameterSpec;
++// BEGIN android-removed
++// import org.bouncycastle.jce.spec.ECParameterSpec;
++// END android-removed
+ 
+ import java.io.ByteArrayInputStream;
+ import java.io.IOException;
+@@ -20,68 +24,74 @@
+                                                    "BC", ConfigurableProvider.EC_IMPLICITLY_CA);
+ 
+     private static ThreadLocal threadSpec = new ThreadLocal();
+-    private static volatile ECParameterSpec ecImplicitCaParams;
++    // BEGIN android-removed
++    // private static volatile ECParameterSpec ecImplicitCaParams;
++    // END android-removed
+ 
+     static void setParameter(String parameterName, Object parameter)
+     {
+         SecurityManager securityManager = System.getSecurityManager();
+ 
+-        if (parameterName.equals(ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA))
+-        {
+-            ECParameterSpec curveSpec;
+-
+-            if (securityManager != null)
+-            {
+-                securityManager.checkPermission(BC_EC_LOCAL_PERMISSION);
+-            }
+-
+-            if (parameter instanceof ECParameterSpec || parameter == null)
+-            {
+-                curveSpec = (ECParameterSpec)parameter;
+-            }
+-            else  // assume java.security.spec
+-            {
+-                curveSpec = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+-            }
+-
+-            if (curveSpec == null)
+-            {
+-                threadSpec.remove();
+-            }
+-            else
+-            {
+-                threadSpec.set(curveSpec);
+-            }
+-        }
+-        else if (parameterName.equals(ConfigurableProvider.EC_IMPLICITLY_CA))
+-        {
+-            if (securityManager != null)
+-            {
+-                securityManager.checkPermission(BC_EC_PERMISSION);
+-            }
+-
+-            if (parameter instanceof ECParameterSpec || parameter == null)
+-            {
+-                ecImplicitCaParams = (ECParameterSpec)parameter;
+-            }
+-            else  // assume java.security.spec
+-            {
+-                ecImplicitCaParams = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+-            }
+-        }
++        // BEGIN android-removed
++        // if (parameterName.equals(ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA))
++        // {
++        //     ECParameterSpec curveSpec;
++        //
++        //     if (securityManager != null)
++        //     {
++        //         securityManager.checkPermission(BC_EC_LOCAL_PERMISSION);
++        //     }
++        //
++        //     if (parameter instanceof ECParameterSpec || parameter == null)
++        //     {
++        //         curveSpec = (ECParameterSpec)parameter;
++        //     }
++        //     else  // assume java.security.spec
++        //     {
++        //         curveSpec = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
++        //     }
++        //
++        //     if (curveSpec == null)
++        //     {
++        //         threadSpec.remove();
++        //     }
++        //     else
++        //     {
++        //         threadSpec.set(curveSpec);
++        //     }
++        // }
++        // else if (parameterName.equals(ConfigurableProvider.EC_IMPLICITLY_CA))
++        // {
++        //     if (securityManager != null)
++        //     {
++        //         securityManager.checkPermission(BC_EC_PERMISSION);
++        //     }
++        //
++        //     if (parameter instanceof ECParameterSpec || parameter == null)
++        //     {
++        //         ecImplicitCaParams = (ECParameterSpec)parameter;
++        //     }
++        //     else  // assume java.security.spec
++        //     {
++        //         ecImplicitCaParams = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
++        //     }
++        // }
++        // END android-removed
+     }
+ 
+-    public static ECParameterSpec getEcImplicitlyCa()
+-    {
+-        ECParameterSpec spec = (ECParameterSpec)threadSpec.get();
+-
+-        if (spec != null)
+-        {
+-            return spec;
+-        }
+-
+-        return ecImplicitCaParams;
+-    }
++    // BEGIN android-removed
++    // public static ECParameterSpec getEcImplicitlyCa()
++    // {
++    //     ECParameterSpec spec = (ECParameterSpec)threadSpec.get();
++    //
++    //     if (spec != null)
++    //     {
++    //         return spec;
++    //     }
++    //
++    //     return ecImplicitCaParams;
++    // }
++    // END android-removed
+ 
+     static int getReadLimit(InputStream in)
+         throws IOException
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java bcprov-jdk16-145/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java	2010-06-25 23:17:26.000000000 +0000
+@@ -1471,7 +1471,11 @@
+         PublicKey workingPublicKey,
+         boolean verificationAlreadyPerformed,
+         X500Principal workingIssuerName,
+-        X509Certificate sign)
++        X509Certificate sign,
++        // BEGIN android-added
++        int i, 
++        boolean trustAnchorInChain)
++        // END android-added
+         throws ExtCertPathValidatorException
+     {
+         List certs = certPath.getCertificates();
+@@ -1485,8 +1489,15 @@
+             {
+                 // (a) (1)
+                 //
++                // BEGIN android-added
++                if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
++                {
++                // END android-added
+                 CertPathValidatorUtilities.verifyX509Certificate(cert, workingPublicKey,
+                     paramsPKIX.getSigProvider());
++                // BEGIN android-added
++                }
++                // END android-added
+             }
+             catch (GeneralSecurityException e)
+             {
+@@ -2077,7 +2088,11 @@
+ 
+     protected static void prepareNextCertK(
+         CertPath certPath,
+-        int index)
++        int index,
++        // BEGIN android-added
++        int i, 
++        boolean trustAnchorInChain)
++        // END android-added
+         throws CertPathValidatorException
+     {
+         List certs = certPath.getCertificates();
+@@ -2105,7 +2120,14 @@
+         }
+         else
+         {
++            // BEGIN android-added
++            if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
++            {
++            // END android-added
+             throw new CertPathValidatorException("Intermediate certificate lacks BasicConstraints");
++            // BEGIN android-added
++            }
++            // END android-added
+         }
+     }
+ 
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/WrapCipherSpi.java bcprov-jdk16-145/org/bouncycastle/jce/provider/WrapCipherSpi.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/WrapCipherSpi.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/WrapCipherSpi.java	2010-06-25 23:17:26.000000000 +0000
+@@ -12,8 +12,10 @@
+ import org.bouncycastle.crypto.Wrapper;
+ import org.bouncycastle.crypto.engines.DESedeEngine;
+ import org.bouncycastle.crypto.engines.DESedeWrapEngine;
+-import org.bouncycastle.crypto.engines.RC2WrapEngine;
+-import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.RC2WrapEngine;
++// import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
++// END android-removed
+ import org.bouncycastle.crypto.params.KeyParameter;
+ import org.bouncycastle.crypto.params.ParametersWithIV;
+ 
+@@ -25,7 +27,9 @@
+ import javax.crypto.ShortBufferException;
+ import javax.crypto.spec.IvParameterSpec;
+ import javax.crypto.spec.PBEParameterSpec;
+-import javax.crypto.spec.RC2ParameterSpec;
++// BEGIN android-removed
++// import javax.crypto.spec.RC2ParameterSpec;
++// END android-removed
+ import javax.crypto.spec.RC5ParameterSpec;
+ import javax.crypto.spec.SecretKeySpec;
+ import java.security.AlgorithmParameters;
+@@ -52,7 +56,9 @@
+                                     {
+                                         IvParameterSpec.class,
+                                         PBEParameterSpec.class,
+-                                        RC2ParameterSpec.class,
++                                        // BEGIN android-removed
++                                        // RC2ParameterSpec.class,
++                                        // END android-removed
+                                         RC5ParameterSpec.class
+                                     };
+ 
+@@ -265,16 +271,19 @@
+         return null;
+     }
+ 
++    // BEGIN android-changed
++    // added ShortBufferException to throws statement
+     protected int engineDoFinal(
+         byte[]  input,
+         int     inputOffset,
+         int     inputLen,
+         byte[]  output,
+         int     outputOffset)
+-        throws IllegalBlockSizeException, BadPaddingException
++        throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
+     {
+         return 0;
+     }
++    // END android-changed
+ 
+     protected byte[] engineWrap(
+         Key     key)
+@@ -307,7 +316,12 @@
+         byte[]  wrappedKey,
+         String  wrappedKeyAlgorithm,
+         int     wrappedKeyType)
+-    throws InvalidKeyException
++    // BEGIN android-removed
++    // throws InvalidKeyException
++    // END android-removed
++    // BEGIN android-added
++    throws InvalidKeyException, NoSuchAlgorithmException
++    // END android-added
+     {
+         byte[] encoded;
+         try
+@@ -354,15 +368,20 @@
+ 
+                 DERObjectIdentifier  oid = in.getAlgorithmId().getObjectId();
+ 
+-                if (oid.equals(X9ObjectIdentifiers.id_ecPublicKey))
+-                {
+-                    privKey = new JCEECPrivateKey(in);
+-                }
+-                else if (oid.equals(CryptoProObjectIdentifiers.gostR3410_94))
+-                {
+-                    privKey = new JDKGOST3410PrivateKey(in);
+-                }
+-                else if (oid.equals(X9ObjectIdentifiers.id_dsa))
++                // BEGIN android-removed
++                // if (oid.equals(X9ObjectIdentifiers.id_ecPublicKey))
++                // {
++                //     privKey = new JCEECPrivateKey(in);
++                // }
++                // else if (oid.equals(CryptoProObjectIdentifiers.gostR3410_94))
++                // {
++                //     privKey = new JDKGOST3410PrivateKey(in);
++                // }
++                // else if (oid.equals(X9ObjectIdentifiers.id_dsa))
++                // END android-removed
++                // BEGIN android-added
++                if (oid.equals(X9ObjectIdentifiers.id_dsa))
++                // END android-added
+                 {
+                     privKey = new JDKDSAPrivateKey(in);
+                 }
+@@ -405,10 +424,12 @@
+             {
+                 throw new InvalidKeyException("Unknown key type " + e.getMessage());
+             }
+-            catch (NoSuchAlgorithmException e)
+-            {
+-                throw new InvalidKeyException("Unknown key type " + e.getMessage());
+-            }
++            // BEGIN android-removed
++            // catch (NoSuchAlgorithmException e)
++            // {
++            //     throw new InvalidKeyException("Unknown key type " + e.getMessage());
++            // }
++            // END android-removed
+             catch (InvalidKeySpecException e2)
+             {
+                 throw new InvalidKeyException("Unknown key type " + e2.getMessage());
+@@ -433,21 +454,23 @@
+         }
+     }
+ 
+-    public static class RC2Wrap
+-        extends WrapCipherSpi
+-    {
+-        public RC2Wrap()
+-        {
+-            super(new RC2WrapEngine());
+-        }
+-    }
+-
+-    public static class RFC3211DESedeWrap
+-        extends WrapCipherSpi
+-    {
+-        public RFC3211DESedeWrap()
+-        {
+-            super(new RFC3211WrapEngine(new DESedeEngine()), 8);
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class RC2Wrap
++    //     extends WrapCipherSpi
++    // {
++    //     public RC2Wrap()
++    //     {
++    //         super(new RC2WrapEngine());
++    //     }
++    // }
++    //
++    // public static class RFC3211DESedeWrap
++    //     extends WrapCipherSpi
++    // {
++    //     public RFC3211DESedeWrap()
++    //     {
++    //         super(new RFC3211WrapEngine(new DESedeEngine()), 8);
++    //     }
++    // }
++    // END android-removed
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/X509CertificateObject.java bcprov-jdk16-145/org/bouncycastle/jce/provider/X509CertificateObject.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/X509CertificateObject.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/X509CertificateObject.java	2010-06-25 23:17:26.000000000 +0000
+@@ -518,12 +518,20 @@
+         return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo());
+     }
+ 
++    // BEGIN android-changed
++    private byte[] encoded;
++    // END android-changed
+     public byte[] getEncoded()
+         throws CertificateEncodingException
+     {
+         try
+         {
+-            return c.getEncoded(ASN1Encodable.DER);
++            // BEGIN android-changed
++            if (encoded == null) {
++                encoded = c.getEncoded(ASN1Encodable.DER);
++            }
++            return encoded;
++            // END android-changed
+         }
+         catch (IOException e)
+         {
+@@ -703,7 +711,7 @@
+     {
+         Signature   signature;
+         String      sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+-        
++
+         try
+         {
+             signature = Signature.getInstance(sigName, "BC");
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/X509SignatureUtil.java bcprov-jdk16-145/org/bouncycastle/jce/provider/X509SignatureUtil.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/X509SignatureUtil.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/X509SignatureUtil.java	2010-06-25 23:17:26.000000000 +0000
+@@ -25,7 +25,9 @@
+ 
+ class X509SignatureUtil
+ {
+-    private static final ASN1Null       derNull = new DERNull();
++    // BEGIN android-changed
++    private static final ASN1Null       derNull = DERNull.INSTANCE;
++    // END android-changed
+     
+     static void setSignatureParameters(
+         Signature signature,
+@@ -66,12 +68,14 @@
+         
+         if (params != null && !derNull.equals(params))
+         {
+-            if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+-            {
+-                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+-                
+-                return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
+-            }
++            // BEGIN android-removed
++            // if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
++            // {
++            //     RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
++            //
++            //     return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
++            // }
++            // END android-removed
+             if (sigAlgId.getObjectId().equals(X9ObjectIdentifiers.ecdsa_with_SHA2))
+             {
+                 ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params);
+@@ -98,10 +102,12 @@
+         {
+             return "SHA1";
+         }
+-        else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
+-        {
+-            return "SHA224";
+-        }
++        // BEGIN android-removed
++        // else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
++        // {
++        //     return "SHA224";
++        // }
++        // END android-removed
+         else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
+         {
+             return "SHA256";
+@@ -114,22 +120,24 @@
+         {
+             return "SHA512";
+         }
+-        else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
+-        {
+-            return "RIPEMD128";
+-        }
+-        else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
+-        {
+-            return "RIPEMD160";
+-        }
+-        else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
+-        {
+-            return "RIPEMD256";
+-        }
+-        else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
+-        {
+-            return "GOST3411";
+-        }
++        // BEGIN android-removed
++        // else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
++        // {
++        //     return "RIPEMD128";
++        // }
++        // else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
++        // {
++        //     return "RIPEMD160";
++        // }
++        // else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
++        // {
++        //     return "RIPEMD256";
++        // }
++        // else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
++        // {
++        //     return "GOST3411";
++        // }
++        // END android-removed
+         else
+         {
+             return digestAlgOID.getId();            
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/symmetric/AES.java bcprov-jdk16-145/org/bouncycastle/jce/provider/symmetric/AES.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/symmetric/AES.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/symmetric/AES.java	2010-06-25 23:17:26.000000000 +0000
+@@ -5,7 +5,9 @@
+ import org.bouncycastle.crypto.engines.AESEngine;
+ import org.bouncycastle.crypto.engines.AESFastEngine;
+ import org.bouncycastle.crypto.engines.AESWrapEngine;
+-import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
++// BEGIN android-removed
++// import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
++// END android-removed
+ import org.bouncycastle.crypto.modes.CBCBlockCipher;
+ import org.bouncycastle.crypto.modes.CFBBlockCipher;
+ import org.bouncycastle.crypto.modes.OFBBlockCipher;
+@@ -36,32 +38,34 @@
+         }
+     }
+ 
+-    public static class CBC
+-       extends JCEBlockCipher
+-    {
+-        public CBC()
+-        {
+-            super(new CBCBlockCipher(new AESFastEngine()), 128);
+-        }
+-    }
+-
+-    static public class CFB
+-        extends JCEBlockCipher
+-    {
+-        public CFB()
+-        {
+-            super(new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 128)), 128);
+-        }
+-    }
+-
+-    static public class OFB
+-        extends JCEBlockCipher
+-    {
+-        public OFB()
+-        {
+-            super(new BufferedBlockCipher(new OFBBlockCipher(new AESFastEngine(), 128)), 128);
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class CBC
++    //    extends JCEBlockCipher
++    // {
++    //     public CBC()
++    //     {
++    //         super(new CBCBlockCipher(new AESFastEngine()), 128);
++    //     }
++    // }
++    //
++    // static public class CFB
++    //     extends JCEBlockCipher
++    // {
++    //     public CFB()
++    //     {
++    //         super(new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 128)), 128);
++    //     }
++    // }
++    //
++    // static public class OFB
++    //     extends JCEBlockCipher
++    // {
++    //     public OFB()
++    //     {
++    //         super(new BufferedBlockCipher(new OFBBlockCipher(new AESFastEngine(), 128)), 128);
++    //     }
++    // }
++    // END android-removed
+ 
+     static public class Wrap
+         extends WrapCipherSpi
+@@ -72,14 +76,16 @@
+         }
+     }
+ 
+-    public static class RFC3211Wrap
+-        extends WrapCipherSpi
+-    {
+-        public RFC3211Wrap()
+-        {
+-            super(new RFC3211WrapEngine(new AESEngine()), 16);
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class RFC3211Wrap
++    //     extends WrapCipherSpi
++    // {
++    //     public RFC3211Wrap()
++    //     {
++    //         super(new RFC3211WrapEngine(new AESEngine()), 16);
++    //     }
++    // }
++    // END android-removed
+ 
+     public static class KeyGen
+         extends JCEKeyGenerator
+@@ -95,70 +101,72 @@
+         }
+     }
+ 
+-    public static class KeyGen128
+-        extends KeyGen
+-    {
+-        public KeyGen128()
+-        {
+-            super(128);
+-        }
+-    }
+-
+-    public static class KeyGen192
+-        extends KeyGen
+-    {
+-        public KeyGen192()
+-        {
+-            super(192);
+-        }
+-    }
+-
+-    public static class KeyGen256
+-        extends KeyGen
+-    {
+-        public KeyGen256()
+-        {
+-            super(256);
+-        }
+-    }
+-
+-    public static class AlgParamGen
+-        extends JDKAlgorithmParameterGenerator
+-    {
+-        protected void engineInit(
+-            AlgorithmParameterSpec genParamSpec,
+-            SecureRandom random)
+-            throws InvalidAlgorithmParameterException
+-        {
+-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for AES parameter generation.");
+-        }
+-
+-        protected AlgorithmParameters engineGenerateParameters()
+-        {
+-            byte[]  iv = new byte[16];
+-
+-            if (random == null)
+-            {
+-                random = new SecureRandom();
+-            }
+-
+-            random.nextBytes(iv);
+-
+-            AlgorithmParameters params;
+-
+-            try
+-            {
+-                params = AlgorithmParameters.getInstance("AES", "BC");
+-                params.init(new IvParameterSpec(iv));
+-            }
+-            catch (Exception e)
+-            {
+-                throw new RuntimeException(e.getMessage());
+-            }
+-
+-            return params;
+-        }
+-    }
++    // BEGIN android-removed
++    // public static class KeyGen128
++    //     extends KeyGen
++    // {
++    //     public KeyGen128()
++    //     {
++    //         super(128);
++    //     }
++    // }
++    //
++    // public static class KeyGen192
++    //     extends KeyGen
++    // {
++    //     public KeyGen192()
++    //     {
++    //         super(192);
++    //     }
++    // }
++    //
++    // public static class KeyGen256
++    //     extends KeyGen
++    // {
++    //     public KeyGen256()
++    //     {
++    //         super(256);
++    //     }
++    // }
++    //
++    // public static class AlgParamGen
++    //     extends JDKAlgorithmParameterGenerator
++    // {
++    //     protected void engineInit(
++    //         AlgorithmParameterSpec genParamSpec,
++    //         SecureRandom random)
++    //         throws InvalidAlgorithmParameterException
++    //     {
++    //         throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for AES parameter generation.");
++    //     }
++    //
++    //     protected AlgorithmParameters engineGenerateParameters()
++    //     {
++    //         byte[]  iv = new byte[16];
++    //
++    //         if (random == null)
++    //         {
++    //             random = new SecureRandom();
++    //         }
++    //
++    //         random.nextBytes(iv);
++    //
++    //         AlgorithmParameters params;
++    //
++    //         try
++    //         {
++    //             params = AlgorithmParameters.getInstance("AES", "BC");
++    //             params.init(new IvParameterSpec(iv));
++    //         }
++    //         catch (Exception e)
++    //         {
++    //             throw new RuntimeException(e.getMessage());
++    //         }
++    //
++    //         return params;
++    //     }
++    // }
++    // END android-removed
+ 
+     public static class AlgParams
+         extends JDKAlgorithmParameters.IVAlgorithmParameters
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/symmetric/AESMappings.java bcprov-jdk16-145/org/bouncycastle/jce/provider/symmetric/AESMappings.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/jce/provider/symmetric/AESMappings.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/jce/provider/symmetric/AESMappings.java	2010-06-25 23:17:26.000000000 +0000
+@@ -26,55 +26,63 @@
+         put("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
+         put("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
+ 
+-        put("AlgorithmParameterGenerator.AES", "org.bouncycastle.jce.provider.symmetric.AES$AlgParamGen");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES128, "AES");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES192, "AES");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES256, "AES");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
+-        put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
++        // BEGIN android-removed
++        // put("AlgorithmParameterGenerator.AES", "org.bouncycastle.jce.provider.symmetric.AES$AlgParamGen");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES128, "AES");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES192, "AES");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES256, "AES");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
++        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
++        // END android-removed
+ 
+         put("Cipher.AES", "org.bouncycastle.jce.provider.symmetric.AES$ECB");
+         put("Alg.Alias.Cipher." + wrongAES128, "AES");
+         put("Alg.Alias.Cipher." + wrongAES192, "AES");
+         put("Alg.Alias.Cipher." + wrongAES256, "AES");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.symmetric.AES$ECB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.symmetric.AES$ECB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.symmetric.AES$ECB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.symmetric.AES$CBC");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.symmetric.AES$CBC");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.symmetric.AES$CBC");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.symmetric.AES$OFB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.symmetric.AES$OFB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.symmetric.AES$OFB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.symmetric.AES$CFB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.symmetric.AES$CFB");
+-        put("Cipher." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.symmetric.AES$CFB");
++        // BEGIN android-changed
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_ECB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_ECB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_ECB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_OFB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_OFB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_OFB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_CFB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_CFB, "AES");
++        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_CFB, "AES");
++        // END android-changed
+         put("Cipher.AESWRAP", "org.bouncycastle.jce.provider.symmetric.AES$Wrap");
+         put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_wrap, "AESWRAP");
+         put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_wrap, "AESWRAP");
+         put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_wrap, "AESWRAP");
+-        put("Cipher.AESRFC3211WRAP", "org.bouncycastle.jce.provider.symmetric.AES$RFC3211Wrap");
++        // BEGIN android-removed
++        // put("Cipher.AESRFC3211WRAP", "org.bouncycastle.jce.provider.symmetric.AES$RFC3211Wrap");
++        // END android-removed
+ 
+         put("KeyGenerator.AES", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen");
+-        put("KeyGenerator.2.16.840.1.101.3.4.2", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator.2.16.840.1.101.3.4.22", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator.2.16.840.1.101.3.4.42", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+-        put("KeyGenerator.AESWRAP", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // BEGIN android-removed
++        // put("KeyGenerator.2.16.840.1.101.3.4.2", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator.2.16.840.1.101.3.4.22", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator.2.16.840.1.101.3.4.42", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // put("KeyGenerator.AESWRAP", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
++        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
++        // END android-removed
+     }
+ }
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/x509/X509Util.java bcprov-jdk16-145/org/bouncycastle/x509/X509Util.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/x509/X509Util.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/x509/X509Util.java	2010-06-25 23:17:26.000000000 +0000
+@@ -43,8 +43,10 @@
+     
+     static
+     {   
+-        algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
+-        algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
++        // BEGIN android-removed
++        // algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
++        // algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
++        // END android-removed
+         algorithms.put("MD5WITHRSAENCRYPTION", PKCSObjectIdentifiers.md5WithRSAEncryption);
+         algorithms.put("MD5WITHRSA", PKCSObjectIdentifiers.md5WithRSAEncryption);
+         algorithms.put("SHA1WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha1WithRSAEncryption);
+@@ -106,19 +108,29 @@
+         //
+         // explicit params
+         //
+-        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA1WITHRSAANDMGF1", creatPSSParams(sha1AlgId, 20));
+ 
+-        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA224WITHRSAANDMGF1", creatPSSParams(sha224AlgId, 28));
+ 
+-        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA256WITHRSAANDMGF1", creatPSSParams(sha256AlgId, 32));
+ 
+-        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA384WITHRSAANDMGF1", creatPSSParams(sha384AlgId, 48));
+ 
+-        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, new DERNull());
++        // BEGIN android-changed
++        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
++        // END android-changed
+         params.put("SHA512WITHRSAANDMGF1", creatPSSParams(sha512AlgId, 64));
+     }
+ 
+@@ -161,7 +173,9 @@
+         }
+         else
+         {
+-            return new AlgorithmIdentifier(sigOid, new DERNull());
++            // BEGIN android-changed
++            return new AlgorithmIdentifier(sigOid, DERNull.INSTANCE);
++            // END android-changed
+         }
+     }
+     
+diff -Naur bcprov-jdk16-145.orig/org/bouncycastle/x509/extension/X509ExtensionUtil.java bcprov-jdk16-145/org/bouncycastle/x509/extension/X509ExtensionUtil.java
+--- bcprov-jdk16-145.orig/org/bouncycastle/x509/extension/X509ExtensionUtil.java	2010-01-11 21:46:14.000000000 +0000
++++ bcprov-jdk16-145/org/bouncycastle/x509/extension/X509ExtensionUtil.java	2010-06-25 23:17:26.000000000 +0000
+@@ -62,7 +62,9 @@
+             {
+                 GeneralName genName = GeneralName.getInstance(it.nextElement());
+                 List list = new ArrayList();
+-                list.add(new Integer(genName.getTagNo()));
++                // BEGIN android-changed
++                list.add(Integer.valueOf(genName.getTagNo()));
++                // END android-changed
+                 switch (genName.getTagNo())
+                 {
+                 case GeneralName.ediPartyName:
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
new file mode 100644
index 0000000..83bc39d
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1ApplicationSpecificParser
+    extends DEREncodable
+{
+    DEREncodable readObject()
+        throws IOException;
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Collection.java b/src/main/java/org/bouncycastle/asn1/ASN1Collection.java
index a51dda1..aa17d3c 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1Collection.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Collection.java
@@ -20,7 +20,7 @@
  * stored.
  */
 public abstract class ASN1Collection
-    extends DERObject
+    extends ASN1Object
 {
     /** &gt;= 0; size of the collection */
     private int size;
@@ -46,7 +46,7 @@
      * @param index the index (starting at zero) of the object
      * @return the object at the postion indicated by index
      */
-    public final DEREncodable getObjectAt(int index) {
+    public DEREncodable getObjectAt(int index) {
         if ((index < 0) || (index >= size)) {
             throw new IndexOutOfBoundsException(Integer.toString(index));
         }
@@ -65,7 +65,7 @@
      *
      * @return the number of objects in this instance
      */
-    public final int size() {
+    public int size() {
         return size;
     }
 
@@ -155,7 +155,7 @@
      * 
      * @return non-null; the enumeration
      */
-    public final Enumeration getObjects() {
+    public Enumeration getObjects() {
         return new ASN1CollectionEnumeration();
     }
 
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java b/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
index 2b993c5..77573a2 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
@@ -3,12 +3,21 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
+/**
+ * Base class for objects which can be written directly to ASN.1 output streams.
+ */
 public abstract class ASN1Encodable
     implements DEREncodable
 {
     public static final String DER = "DER";
     public static final String BER = "BER";
-    
+
+    /**
+     * Return the default BER or DER encoding for this object.
+     *
+     * @return BER/DER byte encoded object.
+     * @throws IOException on encoding error.
+     */
     public byte[] getEncoded() 
         throws IOException
     {
@@ -19,7 +28,14 @@
         
         return bOut.toByteArray();
     }
-    
+
+    /**
+     * Return either the default for "BER" or a DER encoding if "DER" is specified.
+     *
+     * @param encoding name of encoding to use.
+     * @return byte encoded object.
+     * @throws IOException on encoding error.
+     */
     public byte[] getEncoded(
         String encoding) 
         throws IOException
@@ -62,7 +78,12 @@
     public boolean equals(
         Object  o)
     {
-        if ((o == null) || !(o instanceof DEREncodable))
+        if (this == o)
+        {
+            return true;
+        }
+        
+        if (!(o instanceof DEREncodable))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java b/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
index d74e56b..1f50ddf 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -7,4 +7,8 @@
     extends DEREncodableVector
 {
     // migrating from DEREncodeableVector
+    public ASN1EncodableVector()
+    {
+        
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java b/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
index 03f06fe..05f0664 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
@@ -1,12 +1,12 @@
 package org.bouncycastle.asn1;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Vector;
+
+import org.bouncycastle.util.io.Streams;
 
 /**
  * a general purpose ASN.1 decoder - note: this class differs from the
@@ -18,32 +18,13 @@
     extends FilterInputStream
     implements DERTags
 {
-    private DERObject END_OF_STREAM = new DERObject()
-    {
-        void encode(
-            DEROutputStream out)
-        throws IOException
-        {
-            throw new IOException("Eeek!");
-        }
-        public int hashCode()
-        {
-            return 0;
-        }
-        public boolean equals(
-            Object o) 
-        {
-            return o == this;
-        }
-    };
-    
-    boolean eofFound = false;
-    int     limit = Integer.MAX_VALUE;
+    private final int limit;
+    private final boolean lazyEvaluate;
 
     public ASN1InputStream(
         InputStream is)
     {
-        super(is);
+        this(is, Integer.MAX_VALUE);
     }
 
     /**
@@ -57,6 +38,20 @@
     {
         this(new ByteArrayInputStream(input), input.length);
     }
+
+    /**
+     * Create an ASN1InputStream based on the input byte array. The length of DER objects in
+     * the stream is automatically limited to the length of the input array.
+     *
+     * @param input array containing ASN.1 encoded data.
+     * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
+     */
+    public ASN1InputStream(
+        byte[] input,
+        boolean lazyEvaluate)
+    {
+        this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
+    }
     
     /**
      * Create an ASN1InputStream where no DER object will be longer than limit.
@@ -68,17 +63,232 @@
         InputStream input,
         int         limit)
     {
+        this(input, limit, false);
+    }
+
+    /**
+     * Create an ASN1InputStream where no DER object will be longer than limit, and constructed
+     * objects such as sequences will be parsed lazily.
+     *
+     * @param input stream containing ASN.1 encoded data.
+     * @param limit maximum size of a DER encoded object.
+     * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
+     */
+    public ASN1InputStream(
+        InputStream input,
+        int         limit,
+        boolean     lazyEvaluate)
+    {
         super(input);
         this.limit = limit;
+        this.lazyEvaluate = lazyEvaluate;
     }
-    
+
     protected int readLength()
         throws IOException
     {
-        int length = read();
+        return readLength(this, limit);
+    }
+
+    protected void readFully(
+        byte[]  bytes)
+        throws IOException
+    {
+        if (Streams.readFully(this, bytes) != bytes.length)
+        {
+            throw new EOFException("EOF encountered in middle of object");
+        }
+    }
+
+    /**
+     * build an object given its tag and the number of bytes to construct it from.
+     */
+    protected DERObject buildObject(
+        int       tag,
+        int       tagNo,
+        int       length)
+        throws IOException
+    {
+        boolean isConstructed = (tag & CONSTRUCTED) != 0;
+
+        DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length);
+
+        if ((tag & APPLICATION) != 0)
+        {
+            return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+        }
+
+        if ((tag & TAGGED) != 0)
+        {
+            return new BERTaggedObjectParser(tag, tagNo, defIn).getDERObject();
+        }
+
+        if (isConstructed)
+        {
+            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+            switch (tagNo)
+            {
+                case OCTET_STRING:
+                    //
+                    // yes, people actually do this...
+                    //
+                    return new BERConstructedOctetString(buildDEREncodableVector(defIn).v);
+                case SEQUENCE:
+                    if (lazyEvaluate)
+                    {
+                        return new LazyDERSequence(defIn.toByteArray());
+                    }
+                    else
+                    {
+                        return DERFactory.createSequence(buildDEREncodableVector(defIn));   
+                    }
+                case SET:
+                    return DERFactory.createSet(buildDEREncodableVector(defIn), false);
+                case EXTERNAL:
+                    return new DERExternal(buildDEREncodableVector(defIn));                
+                default:
+                    return new DERUnknownTag(true, tagNo, defIn.toByteArray());
+            }
+        }
+
+        return createPrimitiveDERObject(tagNo, defIn.toByteArray());
+    }
+
+    ASN1EncodableVector buildEncodableVector()
+        throws IOException
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        DERObject o;
+
+        while ((o = readObject()) != null)
+        {
+            v.add(o);
+        }
+
+        return v;
+    }
+
+    ASN1EncodableVector buildDEREncodableVector(
+        DefiniteLengthInputStream dIn) throws IOException
+    {
+        return new ASN1InputStream(dIn).buildEncodableVector();
+    }
+
+    public DERObject readObject()
+        throws IOException
+    {
+        int tag = read();
+        if (tag <= 0)
+        {
+            if (tag == 0)
+            {
+                throw new IOException("unexpected end-of-contents marker");
+            }
+
+            return null;
+        }
+
+        //
+        // calculate tag number
+        //
+        int tagNo = readTagNumber(this, tag);
+
+        boolean isConstructed = (tag & CONSTRUCTED) != 0;
+
+        //
+        // calculate length
+        //
+        int length = readLength();
+
+        if (length < 0) // indefinite length method
+        {
+            if (!isConstructed)
+            {
+                throw new IOException("indefinite length primitive encoding encountered");
+            }
+
+            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this);
+
+            if ((tag & APPLICATION) != 0)
+            {
+                ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
+
+                return new BERApplicationSpecificParser(tagNo, sp).getDERObject();
+            }
+            if ((tag & TAGGED) != 0)
+            {
+                return new BERTaggedObjectParser(tag, tagNo, indIn).getDERObject();
+            }
+
+            ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
+
+            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+            switch (tagNo)
+            {
+                case OCTET_STRING:
+                    return new BEROctetStringParser(sp).getDERObject();
+                case SEQUENCE:
+                    return new BERSequenceParser(sp).getDERObject();
+                case SET:
+                    return new BERSetParser(sp).getDERObject();
+                case EXTERNAL:
+                    return new DERExternalParser(sp).getDERObject();
+                default:
+                    throw new IOException("unknown BER object encountered");
+            }
+        }
+        else
+        {
+            return buildObject(tag, tagNo, length);
+        }
+    }
+
+    static int readTagNumber(InputStream s, int tag) 
+        throws IOException
+    {
+        int tagNo = tag & 0x1f;
+
+        //
+        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+        //
+        if (tagNo == 0x1f)
+        {
+            tagNo = 0;
+
+            int b = s.read();
+
+            // X.690-0207 8.1.2.4.2
+            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+            if ((b & 0x7f) == 0) // Note: -1 will pass
+            {
+                throw new IOException("corrupted stream - invalid high tag number found");
+            }
+
+            while ((b >= 0) && ((b & 0x80) != 0))
+            {
+                tagNo |= (b & 0x7f);
+                tagNo <<= 7;
+                b = s.read();
+            }
+
+            if (b < 0)
+            {
+                throw new EOFException("EOF found inside tag value.");
+            }
+            
+            tagNo |= (b & 0x7f);
+        }
+        
+        return tagNo;
+    }
+
+    static int readLength(InputStream s, int limit)
+        throws IOException
+    {
+        int length = s.read();
         if (length < 0)
         {
-            throw new IOException("EOF found when length expected");
+            throw new EOFException("EOF found when length expected");
         }
 
         if (length == 0x80)
@@ -92,440 +302,87 @@
 
             if (size > 4)
             {
-                throw new IOException("DER length more than 4 bytes");
+                throw new IOException("DER length more than 4 bytes: " + size);
             }
-            
+
             length = 0;
             for (int i = 0; i < size; i++)
             {
-                int next = read();
+                int next = s.read();
 
                 if (next < 0)
                 {
-                    throw new IOException("EOF found reading length");
+                    throw new EOFException("EOF found reading length");
                 }
 
                 length = (length << 8) + next;
             }
-            
+
             if (length < 0)
             {
-                throw new IOException("corrupted steam - negative length found");
+                throw new IOException("corrupted stream - negative length found");
             }
-            
+
             if (length >= limit)   // after all we must have read at least 1 byte
             {
-                throw new IOException("corrupted steam - out of bounds length found");
+                throw new IOException("corrupted stream - out of bounds length found");
             }
         }
 
         return length;
     }
 
-    protected void readFully(
+    static DERObject createPrimitiveDERObject(
+        int     tagNo,
         byte[]  bytes)
-        throws IOException
     {
-        int     left = bytes.length;
-        int     len;
-
-        if (left == 0)
+        switch (tagNo)
         {
-            return;
-        }
-
-        while ((len = read(bytes, bytes.length - left, left)) > 0)
-        {
-            if ((left -= len) == 0)
+            case BIT_STRING:
             {
-                return;
+                int padBits = bytes[0];
+                byte[] data = new byte[bytes.length - 1];
+                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+                return new DERBitString(data, padBits);
             }
-        }
-
-        if (left != 0)
-        {
-            throw new EOFException("EOF encountered in middle of object");
-        }
-    }
-
-    /**
-     * build an object given its tag and a byte stream to construct it
-     * from.
-     */
-    protected DERObject buildObject(
-        int       tag,
-        int       tagNo,
-        byte[]    bytes)
-        throws IOException
-    {
-        if ((tag & APPLICATION) != 0)
-        {
-            return new DERApplicationSpecific(tag, bytes);
-        }
-        
-        switch (tag)
-        {
-        case NULL:
-            // BEGIN android-changed
-            return DERNull.THE_ONE;   
-            //END android-changed
-        case SEQUENCE | CONSTRUCTED:
-            ASN1InputStream         aIn = new ASN1InputStream(bytes);
-            ASN1EncodableVector     v = new ASN1EncodableVector();
-
-            DERObject   obj = aIn.readObject();
-
-            while (obj != null)
-            {
-                v.add(obj);
-                obj = aIn.readObject();
-            }
-
-            return new DERSequence(v);
-        case SET | CONSTRUCTED:
-            aIn = new ASN1InputStream(bytes);
-            v = new ASN1EncodableVector();
-
-            obj = aIn.readObject();
-
-            while (obj != null)
-            {
-                v.add(obj);
-                obj = aIn.readObject();
-            }
-
-            return new DERSet(v, false);
-        case BOOLEAN:
-            // BEGIN android-changed
-            return DERBoolean.getInstance(bytes);
-            // END android-changed
-        case INTEGER:
-            return new DERInteger(bytes);
-        case ENUMERATED:
-            return new DEREnumerated(bytes);
-        case OBJECT_IDENTIFIER:
-            return new DERObjectIdentifier(bytes);
-        case BIT_STRING:
-            int     padBits = bytes[0];
-            byte[]  data = new byte[bytes.length - 1];
-
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-
-            return new DERBitString(data, padBits);
-        case NUMERIC_STRING:
-            return new DERNumericString(bytes);
-        case UTF8_STRING:
-            return new DERUTF8String(bytes);
-        case PRINTABLE_STRING:
-            return new DERPrintableString(bytes);
-        case IA5_STRING:
-            return new DERIA5String(bytes);
-        case T61_STRING:
-            return new DERT61String(bytes);
-        case VISIBLE_STRING:
-            return new DERVisibleString(bytes);
-        case GENERAL_STRING:
-            return new DERGeneralString(bytes);
-        case UNIVERSAL_STRING:
-            return new DERUniversalString(bytes);
-        case BMP_STRING:
-            return new DERBMPString(bytes);
-        case OCTET_STRING:
-            return new DEROctetString(bytes);
-        case OCTET_STRING | CONSTRUCTED:
-            return buildDerConstructedOctetString(bytes);
-        case UTC_TIME:
-            return new DERUTCTime(bytes);
-        case GENERALIZED_TIME:
-            return new DERGeneralizedTime(bytes);
-        default:
-            //
-            // with tagged object tag number is bottom 5 bits
-            //
-            
-            if ((tag & TAGGED) != 0)  
-            {
-                if (bytes.length == 0)        // empty tag!
-                {
-                    if ((tag & CONSTRUCTED) == 0)
-                    {
-                        // BEGIN android-changed
-                        return new DERTaggedObject(false, tagNo, DERNull.THE_ONE);
-                        // END android-changed
-                    }
-                    else
-                    {
-                        return new DERTaggedObject(false, tagNo, new DERSequence());
-                    }
-                }
-
-                //
-                // simple type - implicit... return an octet string
-                //
-                if ((tag & CONSTRUCTED) == 0)
-                {
-                    return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
-                }
-
-                aIn = new ASN1InputStream(bytes);
-
-                DEREncodable dObj = aIn.readObject();
-
-                //
-                // explicitly tagged (probably!) - if it isn't we'd have to
-                // tell from the context
-                //
-                if (aIn.available() == 0)
-                {
-                    return new DERTaggedObject(tagNo, dObj);
-                }
-
-                //
-                // another implicit object, we'll create a sequence...
-                //
-                v = new ASN1EncodableVector();
-
-                while (dObj != null)
-                {
-                    v.add(dObj);
-                    dObj = aIn.readObject();
-                }
-
-                return new DERTaggedObject(false, tagNo, new DERSequence(v));
-            }
-
-            return new DERUnknownTag(tag, bytes);
-        }
-    }
-
-    /**
-     * read a string of bytes representing an indefinite length object.
-     */
-    private byte[] readIndefiniteLengthFully()
-        throws IOException
-    {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        int                     b, b1;
-
-        b1 = read();
-
-        while ((b = read()) >= 0)
-        {
-            if (b1 == 0 && b == 0)
-            {
-                break;
-            }
-
-            bOut.write(b1);
-            b1 = b;
-        }
-
-        return bOut.toByteArray();
-    }
-
-    private BERConstructedOctetString buildConstructedOctetString()
-        throws IOException
-    {
-        Vector               octs = new Vector();
-
-        for (;;)
-        {
-            DERObject        o = readObject();
-
-            if (o == END_OF_STREAM)
-            {
-                break;
-            }
-
-            octs.addElement(o);
-        }
-
-        return new BERConstructedOctetString(octs);
-    }
-    
-    //
-    // yes, people actually do this...
-    //
-    private BERConstructedOctetString buildDerConstructedOctetString(byte[] input)
-        throws IOException
-    {
-        Vector               octs = new Vector();
-        ASN1InputStream      aIn = new ASN1InputStream(input);
-        DERObject            o;
-        
-        while ((o = aIn.readObject()) != null)
-        {
-            octs.addElement(o);
-        }
-    
-        return new BERConstructedOctetString(octs);
-    }
-
-    public DERObject readObject()
-        throws IOException
-    {
-        int tag = read();
-        if (tag == -1)
-        {
-            if (eofFound)
-            {
-                throw new EOFException("attempt to read past end of file.");
-            }
-
-            eofFound = true;
-
-            return null;
-        }
-    
-        int tagNo = 0;
-        
-        if ((tag & TAGGED) != 0)  
-        {
-            tagNo = readTagNumber(tag);
-        }
-        
-        int     length = readLength();
-
-        if (length < 0)    // indefinite length method
-        {
-            switch (tag)
-            {
-            case NULL:
+            case BMP_STRING:
+                return new DERBMPString(bytes);
+            case BOOLEAN:
                 // BEGIN android-changed
-                return BERNull.THE_ONE;
+                return DERBoolean.getInstance(bytes);
                 // END android-changed
-            case SEQUENCE | CONSTRUCTED:
-                ASN1EncodableVector  v = new ASN1EncodableVector();
-    
-                for (;;)
-                {
-                    DERObject   obj = readObject();
-
-                    if (obj == END_OF_STREAM)
-                    {
-                        break;
-                    }
-
-                    v.add(obj);
-                }
-                return new BERSequence(v);
-            case SET | CONSTRUCTED:
-                v = new ASN1EncodableVector();
-    
-                for (;;)
-                {
-                    DERObject   obj = readObject();
-
-                    if (obj == END_OF_STREAM)
-                    {
-                        break;
-                    }
-
-                    v.add(obj);
-                }
-                return new BERSet(v, false);
-            case OCTET_STRING | CONSTRUCTED:
-                return buildConstructedOctetString();
+            case ENUMERATED:
+                return new DEREnumerated(bytes);
+            case GENERALIZED_TIME:
+                return new DERGeneralizedTime(bytes);
+            case GENERAL_STRING:
+                return new DERGeneralString(bytes);
+            case IA5_STRING:
+                return new DERIA5String(bytes);
+            case INTEGER:
+                return new DERInteger(bytes);
+            case NULL:
+                return DERNull.INSTANCE;   // actual content is ignored (enforce 0 length?)
+            case NUMERIC_STRING:
+                return new DERNumericString(bytes);
+            case OBJECT_IDENTIFIER:
+                return new DERObjectIdentifier(bytes);
+            case OCTET_STRING:
+                return new DEROctetString(bytes);
+            case PRINTABLE_STRING:
+                return new DERPrintableString(bytes);
+            case T61_STRING:
+                return new DERT61String(bytes);
+            case UNIVERSAL_STRING:
+                return new DERUniversalString(bytes);
+            case UTC_TIME:
+                return new DERUTCTime(bytes);
+            case UTF8_STRING:
+                return new DERUTF8String(bytes);
+            case VISIBLE_STRING:
+                return new DERVisibleString(bytes);
             default:
-                //
-                // with tagged object tag number is bottom 5 bits
-                //
-                if ((tag & TAGGED) != 0)  
-                {
-                    //
-                    // simple type - implicit... return an octet string
-                    //
-                    if ((tag & CONSTRUCTED) == 0)
-                    {
-                        byte[]  bytes = readIndefiniteLengthFully();
-
-                        return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
-                    }
-
-                    //
-                    // either constructed or explicitly tagged
-                    //
-                    DERObject        dObj = readObject();
-
-                    if (dObj == END_OF_STREAM)     // empty tag!
-                    {
-                        return new DERTaggedObject(tagNo);
-                    }
-
-                    DERObject       next = readObject();
-
-                    //
-                    // explicitly tagged (probably!) - if it isn't we'd have to
-                    // tell from the context
-                    //
-                    if (next == END_OF_STREAM)
-                    {
-                        return new BERTaggedObject(tagNo, dObj);
-                    }
-
-                    //
-                    // another implicit object, we'll create a sequence...
-                    //
-                    v = new ASN1EncodableVector();
-
-                    v.add(dObj);
-
-                    do
-                    {
-                        v.add(next);
-                        next = readObject();
-                    }
-                    while (next != END_OF_STREAM);
-
-                    return new BERTaggedObject(false, tagNo, new BERSequence(v));
-                }
-
-                throw new IOException("unknown BER object encountered");
-            }
+                return new DERUnknownTag(false, tagNo, bytes);
         }
-        else
-        {
-            if (tag == 0 && length == 0)    // end of contents marker.
-            {
-                return END_OF_STREAM;
-            }
-
-            byte[]  bytes = new byte[length];
-    
-            readFully(bytes);
-    
-            return buildObject(tag, tagNo, bytes);
-        }
-    }
-
-    private int readTagNumber(int tag) 
-        throws IOException
-    {
-        int tagNo = tag & 0x1f;
-
-        if (tagNo == 0x1f)
-        {
-            int b = read();
-
-            tagNo = 0;
-
-            while ((b >= 0) && ((b & 0x80) != 0))
-            {
-                tagNo |= (b & 0x7f);
-                tagNo <<= 7;
-                b = read();
-            }
-
-            if (b < 0)
-            {
-                eofFound = true;
-                throw new EOFException("EOF found inside tag value.");
-            }
-            
-            tagNo |= (b & 0x7f);
-        }
-        
-        return tagNo;
     }
 }
-
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/src/main/java/org/bouncycastle/asn1/ASN1Null.java
index dcb823d..539e7db 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1Null.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Null.java
@@ -6,7 +6,7 @@
  * A NULL object.
  */
 public abstract class ASN1Null
-    extends DERObject
+    extends ASN1Object
 {
     // BEGIN android-changed
     /*package*/ ASN1Null()
@@ -16,13 +16,13 @@
 
     public int hashCode()
     {
-        return 0;
+        return -1;
     }
 
-    public boolean equals(
-        Object o)
+    boolean asn1Equals(
+        DERObject o)
     {
-        if ((o == null) || !(o instanceof ASN1Null))
+        if (!(o instanceof ASN1Null))
         {
             return false;
         }
@@ -35,6 +35,6 @@
 
     public String toString()
     {
-      return "NULL";
+         return "NULL";
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Object.java b/src/main/java/org/bouncycastle/asn1/ASN1Object.java
new file mode 100644
index 0000000..7a0b113
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Object.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public abstract  class ASN1Object
+    extends DERObject
+{
+    /**
+     * Create a base ASN.1 object from a byte stream.
+     *
+     * @param data the byte stream to parse.
+     * @return the base ASN.1 object represented by the byte stream.
+     * @exception IOException if there is a problem parsing the data.
+     */
+    public static ASN1Object fromByteArray(byte[] data)
+        throws IOException
+    {
+        ASN1InputStream aIn = new ASN1InputStream(data);
+
+        return (ASN1Object)aIn.readObject();
+    }
+
+    public final boolean equals(Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        
+        return (o instanceof DEREncodable) && asn1Equals(((DEREncodable)o).getDERObject());
+    }
+
+    public abstract int hashCode();
+
+    abstract void encode(DEROutputStream out) throws IOException;
+
+    abstract boolean asn1Equals(DERObject o);
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ObjectParser.java b/src/main/java/org/bouncycastle/asn1/ASN1ObjectParser.java
new file mode 100644
index 0000000..ff09a45
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1ObjectParser.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+
+/**
+ * @deprecated will be removed
+ */
+public class ASN1ObjectParser
+{
+    ASN1StreamParser _aIn;
+
+    protected ASN1ObjectParser(
+        int         baseTag,
+        int         tagNumber,
+        InputStream contentStream)
+    {
+        _aIn = new ASN1StreamParser(contentStream);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
index a65d268..b1d72a2 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
@@ -1,13 +1,17 @@
 package org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.Arrays;
+
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Enumeration;
 import java.util.Vector;
-import org.bouncycastle.util.encoders.Hex;
 
 public abstract class ASN1OctetString
-    extends DERObject
+    extends ASN1Object
+    implements ASN1OctetStringParser
 {
     byte[]  string;
 
@@ -68,6 +72,10 @@
     public ASN1OctetString(
         byte[]  string)
     {
+        if (string == null)
+        {
+            throw new NullPointerException("string cannot be null");
+        }
         this.string = string;
     }
 
@@ -76,13 +84,7 @@
     {
         try
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(obj);
-            dOut.close();
-
-            this.string = bOut.toByteArray();
+            this.string = obj.getDERObject().getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
@@ -90,6 +92,16 @@
         }
     }
 
+    public InputStream getOctetStream()
+    {
+        return new ByteArrayInputStream(string);
+    }
+
+    public ASN1OctetStringParser parser()
+    {
+        return this;
+    }
+
     public byte[] getOctets()
     {
         return string;
@@ -97,44 +109,20 @@
 
     public int hashCode()
     {
-        byte[]  b = this.getOctets();
-        int     value = 0;
-
-        for (int i = 0; i != b.length; i++)
-        {
-            value ^= (b[i] & 0xff) << (i % 4);
-        }
-
-        return value;
+        return Arrays.hashCode(this.getOctets());
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if (!(o instanceof DEROctetString))
+        if (!(o instanceof ASN1OctetString))
         {
             return false;
         }
 
-        DEROctetString  other = (DEROctetString)o;
+        ASN1OctetString  other = (ASN1OctetString)o;
 
-        byte[] b1 = other.getOctets();
-        byte[] b2 = this.getOctets();
-
-        if (b1.length != b2.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != b1.length; i++)
-        {
-            if (b1[i] != b2[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return Arrays.areEqual(string, other.string);
     }
 
     abstract void encode(DEROutputStream out)
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java b/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
new file mode 100644
index 0000000..641020c
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+
+public interface ASN1OctetStringParser
+    extends DEREncodable
+{
+    public InputStream getOctetStream();
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java b/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
new file mode 100644
index 0000000..2abdb18
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.asn1;
+
+public class ASN1ParsingException
+    extends IllegalStateException
+{
+    private Throwable cause;
+
+    ASN1ParsingException(String message)
+    {
+        super(message);
+    }
+
+    ASN1ParsingException(String message, Throwable cause)
+    {
+        super(message);
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
index 400b1dc..e31e673 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
@@ -1,11 +1,9 @@
 package org.bouncycastle.asn1;
 
-// BEGIN android-removed
-//import java.io.IOException;
-// END android-removed
+import java.io.IOException;
 import java.util.Enumeration;
 // BEGIN android-removed
-//import java.util.Vector;
+// import java.util.Vector;
 // END android-removed
 
 // BEGIN android-note
@@ -33,13 +31,13 @@
             return (ASN1Sequence)obj;
         }
 
-        throw new IllegalArgumentException("unknown object in getInstance");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     /**
      * Return an ASN1 sequence from a tagged object. There is a special
      * case here, if an object appears to have been explicitly tagged on 
-     * reading but we were expecting it to be implictly tagged in the 
+     * reading but we were expecting it to be implicitly tagged in the 
      * normal course of events it indicates that we lost the surrounding
      * sequence - so we need to add it back (this will happen if the tagged
      * object is a sequence that contains other sequences). If you are
@@ -92,78 +90,104 @@
             }
         }
 
-        throw new IllegalArgumentException(
-                "unknown object in getInstanceFromTagged");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     // BEGIN android-removed
-    //public Enumeration getObjects()
-    //{
-    //    return seq.elements();
-    //}
-
-    ///**
-    // * return the object at the sequence postion indicated by index.
-    // *
-    // * @param index the sequence number (starting at zero) of the object
-    // * @return the object at the sequence postion indicated by index.
-    // */
-    //public DEREncodable getObjectAt(
-    //    int index)
-    //{
-    //    return (DEREncodable)seq.elementAt(index);
-    //}
-
-    ///**
-    // * return the number of objects in this sequence.
-    // *
-    // * @return the number of objects in this sequence.
-    // */
-    //public int size()
-    //{
-    //    return seq.size();
-    //}
-
-    //public int hashCode()
-    //{
-    //    Enumeration             e = this.getObjects();
-    //    int                     hashCode = 0;
-
-    //    while (e.hasMoreElements())
-    //    {
-    //        Object    o = e.nextElement();
-    //        
-    //        if (o != null)
-    //        {
-    //            hashCode ^= o.hashCode();
-    //        }
-    //    }
-
-    //    return hashCode;
-    //}
+    // public Enumeration getObjects()
+    // {
+    //     return seq.elements();
+    // }
     // END android-removed
 
-    public boolean equals(
-        Object  o)
+    public ASN1SequenceParser parser()
     {
-        if (o == this)
-        {
-            return true;
-        }
-        
-        if (!(o instanceof DEREncodable))
-        {
-            return false;
-        }
+        final ASN1Sequence outer = this;
 
-        DERObject      dObj = ((DEREncodable)o).getDERObject();
-        
-        if (!(dObj instanceof ASN1Sequence))
+        return new ASN1SequenceParser()
+        {
+            private final int max = size();
+
+            private int index;
+
+            public DEREncodable readObject() throws IOException
+            {
+                if (index == max)
+                {
+                    return null;
+                }
+                
+                DEREncodable obj = getObjectAt(index++);
+                if (obj instanceof ASN1Sequence)
+                {
+                    return ((ASN1Sequence)obj).parser();
+                }
+                if (obj instanceof ASN1Set)
+                {
+                    return ((ASN1Set)obj).parser();
+                }
+
+                return obj;
+            }
+
+            public DERObject getDERObject()
+            {
+                return outer;
+            }
+        };
+    }
+
+    // BEGIN android-removed
+    // /**
+    //  * return the object at the sequence position indicated by index.
+    //  *
+    //  * @param index the sequence number (starting at zero) of the object
+    //  * @return the object at the sequence position indicated by index.
+    //  */
+    // public DEREncodable getObjectAt(
+    //     int index)
+    // {
+    //     return (DEREncodable)seq.elementAt(index);
+    // }
+    //
+    // /**
+    //  * return the number of objects in this sequence.
+    //  *
+    //  * @return the number of objects in this sequence.
+    //  */
+    // public int size()
+    // {
+    //     return seq.size();
+    // }
+    //
+    // public int hashCode()
+    // {
+    //     Enumeration             e = this.getObjects();
+    //     int                     hashCode = size();
+    //
+    //     while (e.hasMoreElements())
+    //     {
+    //         Object o = e.nextElement();
+    //         hashCode *= 17;
+    //         if (o != null)
+    //         {
+    //             hashCode ^= o.hashCode();
+    //         }
+    //     }
+    //
+    //     return hashCode;
+    // }
+    // END android-removed
+
+    boolean asn1Equals(
+        DERObject  o)
+    {
+        if (!(o instanceof ASN1Sequence))
         {
             return false;
         }
         
-        ASN1Sequence   other = (ASN1Sequence)dObj;
+        ASN1Sequence   other = (ASN1Sequence)o;
 
         if (this.size() != other.size())
         {
@@ -175,24 +199,15 @@
 
         while (s1.hasMoreElements())
         {
-            Object  o1 = s1.nextElement();
-            Object  o2 = s2.nextElement();
+            DERObject  o1 = ((DEREncodable)s1.nextElement()).getDERObject();
+            DERObject  o2 = ((DEREncodable)s2.nextElement()).getDERObject();
 
-            if (o1 != null && o2 != null)
-            {
-                if (!o1.equals(o2))
-                {
-                    return false;
-                }
-            }
-            else if (o1 == null && o2 == null)
+            if (o1 == o2 || (o1 != null && o1.equals(o2)))
             {
                 continue;
             }
-            else
-            {
-                return false;
-            }
+
+            return false;
         }
 
         return true;
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java b/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
new file mode 100644
index 0000000..ceda6bd
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1SequenceParser
+    extends DEREncodable
+{
+    DEREncodable readObject()
+        throws IOException;
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/src/main/java/org/bouncycastle/asn1/ASN1Set.java
index 9e6c55f..88a20ee 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1Set.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1Set.java
@@ -1,13 +1,11 @@
 package org.bouncycastle.asn1;
 
-// BEGIN android-removed
-//import java.io.ByteArrayOutputStream;
-//import java.io.IOException;
-// END android-removed
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.Enumeration;
 // BEGIN android-removed
-//import java.util.Vector;
-// END android-removed;
+// import java.util.Vector;
+// END android-removed
 
 // BEGIN android-note
 // Changed inheritence of class.
@@ -17,7 +15,7 @@
     extends ASN1Collection
 {
     // BEGIN android-removed
-    //protected Vector set = new Vector();
+    // protected Vector set = new Vector();
     // END android-removed
 
     /**
@@ -34,13 +32,13 @@
             return (ASN1Set)obj;
         }
 
-        throw new IllegalArgumentException("unknown object in getInstance");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     /**
      * Return an ASN1 set from a tagged object. There is a special
      * case here, if an object appears to have been explicitly tagged on 
-     * reading but we were expecting it to be implictly tagged in the 
+     * reading but we were expecting it to be implicitly tagged in the 
      * normal course of events it indicates that we lost the surrounding
      * set - so we need to add it back (this will happen if the tagged
      * object is a sequence that contains other sequences). If you are
@@ -107,8 +105,7 @@
             }
         }
 
-        throw new IllegalArgumentException(
-                    "unknown object in getInstanceFromTagged");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     public ASN1Set()
@@ -116,68 +113,100 @@
     }
 
     // BEGIN android-removed
-    //public Enumeration getObjects()
-    //{
-    //    return set.elements();
-    //}
-
-    ///**
-    // * return the object at the set postion indicated by index.
-    // *
-    // * @param index the set number (starting at zero) of the object
-    // * @return the object at the set postion indicated by index.
-    // */
-    //public DEREncodable getObjectAt(
-    //    int index)
-    //{
-    //    return (DEREncodable)set.elementAt(index);
-    //}
-
-    ///**
-    // * return the number of objects in this set.
-    // *
-    // * @return the number of objects in this set.
-    // */
-    //public int size()
-    //{
-    //    return set.size();
-    //}
-
-    //public int hashCode()
-    //{
-    //    Enumeration             e = this.getObjects();
-    //    int                     hashCode = 0;
-
-    //    while (e.hasMoreElements())
-    //    {
-    //        hashCode ^= e.nextElement().hashCode();
-    //    }
-
-    //    return hashCode;
-    //}
+    // public Enumeration getObjects()
+    // {
+    //     return set.elements();
+    // }
+    //
+    // /**
+    //  * return the object at the set position indicated by index.
+    //  *
+    //  * @param index the set number (starting at zero) of the object
+    //  * @return the object at the set position indicated by index.
+    //  */
+    // public DEREncodable getObjectAt(
+    //     int index)
+    // {
+    //     return (DEREncodable)set.elementAt(index);
+    // }
+    //
+    // /**
+    //  * return the number of objects in this set.
+    //  *
+    //  * @return the number of objects in this set.
+    //  */
+    // public int size()
+    // {
+    //     return set.size();
+    // }
     // END android-removed
 
-    public boolean equals(
-        Object  o)
+    public ASN1SetParser parser()
     {
-        if (o == this)
+        final ASN1Set outer = this;
+
+        return new ASN1SetParser()
         {
-            return true;
-        }
-        
-        if (!(o instanceof DEREncodable))
+            private final int max = size();
+
+            private int index;
+
+            public DEREncodable readObject() throws IOException
+            {
+                if (index == max)
+                {
+                    return null;
+                }
+
+                DEREncodable obj = getObjectAt(index++);
+                if (obj instanceof ASN1Sequence)
+                {
+                    return ((ASN1Sequence)obj).parser();
+                }
+                if (obj instanceof ASN1Set)
+                {
+                    return ((ASN1Set)obj).parser();
+                }
+
+                return obj;
+            }
+
+            public DERObject getDERObject()
+            {
+                return outer;
+            }
+        };
+    }
+
+    // BEGIN android-removed
+    // public int hashCode()
+    // {
+    //     Enumeration             e = this.getObjects();
+    //     int                     hashCode = size();
+    //
+    //     while (e.hasMoreElements())
+    //     {
+    //         Object o = e.nextElement();
+    //         hashCode *= 17;
+    //         if (o != null)
+    //         {
+    //             hashCode ^= o.hashCode();
+    //         }
+    //     }
+    //
+    //     return hashCode;
+    // }
+    // END android-removed
+
+    boolean asn1Equals(
+        DERObject  o)
+    {
+        if (!(o instanceof ASN1Set))
         {
             return false;
         }
 
-        DERObject      dObj = ((DEREncodable)o).getDERObject();
-        
-        if (!(dObj instanceof ASN1Set))
-        {
-            return false;
-        }
-
-        ASN1Set   other = (ASN1Set)dObj;
+        ASN1Set   other = (ASN1Set)o;
 
         if (this.size() != other.size())
         {
@@ -189,130 +218,142 @@
 
         while (s1.hasMoreElements())
         {
-            if (!s1.nextElement().equals(s2.nextElement()))
+            DERObject  o1 = ((DEREncodable)s1.nextElement()).getDERObject();
+            DERObject  o2 = ((DEREncodable)s2.nextElement()).getDERObject();
+
+            if (o1 == o2 || (o1 != null && o1.equals(o2)))
             {
-                return false;
+                continue;
             }
+
+            return false;
         }
 
         return true;
     }
 
     // BEGIN android-removed
-    ///**
-    // * return true if a <= b (arrays are assumed padded with zeros).
-    // */
-    //private boolean lessThanOrEqual(
-    //     byte[] a,
-    //     byte[] b)
-    //{
-    //     if (a.length <= b.length)
+    // /**
+    //  * return true if a <= b (arrays are assumed padded with zeros).
+    //  */
+    // private boolean lessThanOrEqual(
+    //      byte[] a,
+    //      byte[] b)
+    // {
+    //      if (a.length <= b.length)
+    //      {
+    //          for (int i = 0; i != a.length; i++)
+    //          {
+    //              int    l = a[i] & 0xff;
+    //              int    r = b[i] & 0xff;
+    //
+    //              if (r > l)
+    //              {
+    //                  return true;
+    //              }
+    //              else if (l > r)
+    //              {
+    //                  return false;
+    //              }
+    //          }
+    //
+    //          return true;
+    //      }
+    //      else
+    //      {
+    //          for (int i = 0; i != b.length; i++)
+    //          {
+    //              int    l = a[i] & 0xff;
+    //              int    r = b[i] & 0xff;
+    //
+    //              if (r > l)
+    //              {
+    //                  return true;
+    //              }
+    //              else if (l > r)
+    //              {
+    //                  return false;
+    //              }
+    //          }
+    //
+    //          return false;
+    //      }
+    // }
+    // END android-removed
+
+    private byte[] getEncoded(
+        DEREncodable obj)
+    {
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+        ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
+
+        try
+        {
+            aOut.writeObject(obj);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("cannot encode object added to SET");
+        }
+
+        return bOut.toByteArray();
+    }
+
+    // BEGIN android-removed
+    // protected void sort()
+    // {
+    //     if (set.size() > 1)
     //     {
-    //         for (int i = 0; i != a.length; i++)
+    //         boolean    swapped = true;
+    //         int        lastSwap = set.size() - 1;
+    //
+    //         while (swapped)
     //         {
-    //             int    l = a[i] & 0xff;
-    //             int    r = b[i] & 0xff;
-    //             
-    //             if (r > l)
+    //             int    index = 0;
+    //             int    swapIndex = 0;
+    //             byte[] a = getEncoded((DEREncodable)set.elementAt(0));
+    //
+    //             swapped = false;
+    //
+    //             while (index != lastSwap)
     //             {
-    //                 return true;
+    //                 byte[] b = getEncoded((DEREncodable)set.elementAt(index + 1));
+    //
+    //                 if (lessThanOrEqual(a, b))
+    //                 {
+    //                     a = b;
+    //                 }
+    //                 else
+    //                 {
+    //                     Object  o = set.elementAt(index);
+    //
+    //                     set.setElementAt(set.elementAt(index + 1), index);
+    //                     set.setElementAt(o, index + 1);
+    //
+    //                     swapped = true;
+    //                     swapIndex = index;
+    //                 }
+    //
+    //                 index++;
     //             }
-    //             else if (l > r)
-    //             {
-    //                 return false;
-    //             }
+    //
+    //             lastSwap = swapIndex;
     //         }
-
-    //         return true;
     //     }
-    //     else
-    //     {
-    //         for (int i = 0; i != b.length; i++)
-    //         {
-    //             int    l = a[i] & 0xff;
-    //             int    r = b[i] & 0xff;
-    //             
-    //             if (r > l)
-    //             {
-    //                 return true;
-    //             }
-    //             else if (l > r)
-    //             {
-    //                 return false;
-    //             }
-    //         }
-
-    //         return false;
-    //     }
-    //}
-
-    //private byte[] getEncoded(
-    //    DEREncodable obj)
-    //{
-    //    ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-    //    ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
-
-    //    try
-    //    {
-    //        aOut.writeObject(obj);
-    //    }
-    //    catch (IOException e)
-    //    {
-    //        throw new IllegalArgumentException("cannot encode object added to SET");
-    //    }
-
-    //    return bOut.toByteArray();
-    //}
-
-    //protected void sort()
-    //{
-    //    if (set.size() > 1)
-    //    {
-    //        boolean    swapped = true;
-
-    //        while (swapped)
-    //        {
-    //            int    index = 0;
-    //            byte[] a = getEncoded((DEREncodable)set.elementAt(0));
-    //            
-    //            swapped = false;
-    //            
-    //            while (index != set.size() - 1)
-    //            {
-    //                byte[] b = getEncoded((DEREncodable)set.elementAt(index + 1));
-
-    //                if (lessThanOrEqual(a, b))
-    //                {
-    //                    a = b;
-    //                }
-    //                else
-    //                {
-    //                    Object  o = set.elementAt(index);
-
-    //                    set.setElementAt(set.elementAt(index + 1), index);
-    //                    set.setElementAt(o, index + 1);
-
-    //                    swapped = true;
-    //                }
-
-    //                index++;
-    //            }
-    //        }
-    //    }
-    //}
-
-    //protected void addObject(
-    //    DEREncodable obj)
-    //{
-    //    set.addElement(obj);
-    //}
-
-    //abstract void encode(DEROutputStream out)
-    //        throws IOException;
-
-    //public String toString() 
-    //{
-    //  return set.toString();
-    //}
+    // }
+    //
+    // protected void addObject(
+    //     DEREncodable obj)
+    // {
+    //     set.addElement(obj);
+    // }
+    //
+    // abstract void encode(DEROutputStream out)
+    //         throws IOException;
+    //
+    // public String toString() 
+    // {
+    //   return set.toString();
+    // }
     // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java b/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
new file mode 100644
index 0000000..b09a170
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1SetParser
+    extends DEREncodable
+{
+    public DEREncodable readObject()
+        throws IOException;
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java b/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
new file mode 100644
index 0000000..43fcad7
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ASN1StreamParser
+{
+    private final InputStream _in;
+    private final int         _limit;
+
+    private static int findLimit(InputStream in)
+    {
+        if (in instanceof DefiniteLengthInputStream)
+        {
+            return ((DefiniteLengthInputStream)in).getRemaining();
+        }
+
+        return Integer.MAX_VALUE;
+    }
+
+    public ASN1StreamParser(
+        InputStream in)
+    {
+        this(in, findLimit(in));
+    }
+
+    public ASN1StreamParser(
+        InputStream in,
+        int         limit)
+    {
+        this._in = in;
+        this._limit = limit;
+    }
+
+    public ASN1StreamParser(
+        byte[] encoding)
+    {
+        this(new ByteArrayInputStream(encoding), encoding.length);
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        int tag = _in.read();
+        if (tag == -1)
+        {
+            return null;
+        }
+
+        //
+        // turn of looking for "00" while we resolve the tag
+        //
+        set00Check(false);
+
+        //
+        // calculate tag number
+        //
+        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
+
+        boolean isConstructed = (tag & DERTags.CONSTRUCTED) != 0;
+
+        //
+        // calculate length
+        //
+        int length = ASN1InputStream.readLength(_in, _limit);
+
+        if (length < 0) // indefinite length method
+        {
+            if (!isConstructed)
+            {
+                throw new IOException("indefinite length primitive encoding encountered");
+            }
+
+            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in);
+
+            if ((tag & DERTags.APPLICATION) != 0)
+            {
+                ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+
+                return new BERApplicationSpecificParser(tagNo, sp);
+            }
+
+            if ((tag & DERTags.TAGGED) != 0)
+            {
+                return new BERTaggedObjectParser(tag, tagNo, indIn);
+            }
+
+            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+
+            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+            switch (tagNo)
+            {
+                case DERTags.OCTET_STRING:
+                    return new BEROctetStringParser(sp);
+                case DERTags.SEQUENCE:
+                    return new BERSequenceParser(sp);
+                case DERTags.SET:
+                    return new BERSetParser(sp);
+                case DERTags.EXTERNAL:{
+                    return new DERExternalParser(sp);
+                }
+                default:
+                    throw new IOException("unknown BER object encountered: 0x" + Integer.toHexString(tagNo));
+            }
+        }
+        else
+        {
+            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
+
+            if ((tag & DERTags.APPLICATION) != 0)
+            {
+                return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+            }
+
+            if ((tag & DERTags.TAGGED) != 0)
+            {
+                return new BERTaggedObjectParser(tag, tagNo, defIn);
+            }
+
+            if (isConstructed)
+            {
+                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+                switch (tagNo)
+                {
+                    case DERTags.OCTET_STRING:
+                        //
+                        // yes, people actually do this...
+                        //
+                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
+                    case DERTags.SEQUENCE:
+                        return new DERSequenceParser(new ASN1StreamParser(defIn));
+                    case DERTags.SET:
+                        return new DERSetParser(new ASN1StreamParser(defIn));
+                    case DERTags.EXTERNAL:
+                        return new DERExternalParser(new ASN1StreamParser(defIn));
+                    default:
+                        // TODO Add DERUnknownTagParser class?
+                        return new DERUnknownTag(true, tagNo, defIn.toByteArray());
+                }
+            }
+
+            // Some primitive encodings can be handled by parsers too...
+            switch (tagNo)
+            {
+                case DERTags.OCTET_STRING:
+                    return new DEROctetStringParser(defIn);
+            }
+
+            return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn.toByteArray());
+        }
+    }
+
+    private void set00Check(boolean enabled)
+    {
+        if (_in instanceof IndefiniteLengthInputStream)
+        {
+            ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
+        }
+    }
+
+    ASN1EncodableVector readVector() throws IOException
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        DEREncodable obj;
+        while ((obj = readObject()) != null)
+        {
+            v.add(obj.getDERObject());
+        }
+
+        return v;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
index 5d6a2a4..1e5d4e8 100644
--- a/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
+++ b/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -3,12 +3,13 @@
 import java.io.IOException;
 
 /**
- * ASN.1 TaggedObject - in ASN.1 nottation this is any object proceeded by
- * a [n] where n is some number - these are assume to follow the construction
+ * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
  * rules (as with sequences).
  */
 public abstract class ASN1TaggedObject
-    extends DERObject
+    extends ASN1Object
+    implements ASN1TaggedObjectParser
 {
     int             tagNo;
     boolean         empty = false;
@@ -35,7 +36,7 @@
                 return (ASN1TaggedObject)obj;
         }
 
-        throw new IllegalArgumentException("unknown object in getInstance");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     /**
@@ -81,8 +82,8 @@
         this.obj = obj;
     }
     
-    public boolean equals(
-        Object o)
+    boolean asn1Equals(
+        DERObject o)
     {
         if (!(o instanceof ASN1TaggedObject))
         {
@@ -98,14 +99,14 @@
         
         if(obj == null)
         {
-            if(other.obj != null)
+            if (other.obj != null)
             {
                 return false;
             }
         }
         else
         {
-            if(!(obj.equals(other.obj)))
+            if (!(obj.getDERObject().equals(other.obj.getDERObject())))
             {
                 return false;
             }
@@ -118,6 +119,11 @@
     {
         int code = tagNo;
 
+        // TODO: actually this is wrong - the problem is that a re-encoded
+        // object may end up with a different hashCode due to implicit
+        // tagging. As implicit tagging is ambiguous if a sequence is involved
+        // it seems the only correct method for both equals and hashCode is to
+        // compare the encodings...
         if (obj != null)
         {
             code ^= obj.hashCode();
@@ -167,6 +173,33 @@
         return null;
     }
 
+    /**
+     * Return the object held in this tagged object as a parser assuming it has
+     * the type of the passed in tag. If the object doesn't have a parser
+     * associated with it, the base object is returned.
+     */
+    public DEREncodable getObjectParser(
+        int     tag,
+        boolean isExplicit)
+    {
+        switch (tag)
+        {
+        case DERTags.SET:
+            return ASN1Set.getInstance(this, isExplicit).parser();
+        case DERTags.SEQUENCE:
+            return ASN1Sequence.getInstance(this, isExplicit).parser();
+        case DERTags.OCTET_STRING:
+            return ASN1OctetString.getInstance(this, isExplicit).parser();
+        }
+
+        if (isExplicit)
+        {
+            return getObject();
+        }
+
+        throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
+    }
+
     abstract void encode(DEROutputStream  out)
         throws IOException;
 
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
new file mode 100644
index 0000000..5574bf8
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public interface ASN1TaggedObjectParser
+    extends DEREncodable
+{
+    public int getTagNo();
+    
+    public DEREncodable getObjectParser(int tag, boolean isExplicit)
+        throws IOException;
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java b/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
new file mode 100644
index 0000000..8bc8a4e
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.asn1;
+
+public class BERApplicationSpecific
+    extends DERApplicationSpecific
+{
+    public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+    {
+        super(tagNo, vec);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
new file mode 100644
index 0000000..0c46ba3
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERApplicationSpecificParser
+    implements ASN1ApplicationSpecificParser
+{
+    private final int tag;
+    private final ASN1StreamParser parser;
+
+    BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
+    {
+        this.tag = tag;
+        this.parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return parser.readObject();
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new BERApplicationSpecific(tag, parser.readVector());
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java b/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
index c9518a6..7e712c3 100644
--- a/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
+++ b/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
@@ -8,6 +8,8 @@
 public class BERConstructedOctetString
     extends DEROctetString
 {
+    private static final int MAX_LENGTH = 1000;
+
     /**
      * convert a vector of octet strings into a single byte string
      */
@@ -86,33 +88,30 @@
         return octs.elements();
     }
 
-    private Vector generateOcts()
-    {
-        int     start = 0;
-        int     end = 0;
-        Vector  vec = new Vector();
+    private Vector generateOcts() 
+    { 
+        Vector vec = new Vector(); 
+        for (int i = 0; i < string.length; i += MAX_LENGTH) 
+        { 
+            int end; 
 
-        while ((end + 1) < string.length)
-        {
-            if (string[end] == 0 && string[end + 1] == 0)
-            {
-                byte[]  nStr = new byte[end - start + 1];
+            if (i + MAX_LENGTH > string.length) 
+            { 
+                end = string.length; 
+            } 
+            else 
+            { 
+                end = i + MAX_LENGTH; 
+            } 
 
-                System.arraycopy(string, start, nStr, 0, nStr.length);
+            byte[] nStr = new byte[end - i]; 
 
-                vec.addElement(new DEROctetString(nStr));
-                start = end + 1;
-            }
-            end++;
-        }
+            System.arraycopy(string, i, nStr, 0, nStr.length); 
 
-        byte[]  nStr = new byte[string.length - start];
-
-        System.arraycopy(string, start, nStr, 0, nStr.length);
-
-        vec.addElement(new DEROctetString(nStr));
-
-        return vec;
+            vec.addElement(new DEROctetString(nStr)); 
+         } 
+        
+         return vec; 
     }
 
     public void encode(
@@ -128,37 +127,10 @@
             //
             // write out the octet array
             //
-            if (octs != null)
+            Enumeration e = getObjects();
+            while (e.hasMoreElements())
             {
-                for (int i = 0; i != octs.size(); i++)
-                {
-                    out.writeObject(octs.elementAt(i));
-                }
-            }
-            else
-            {
-                int     start = 0;
-                int     end = 0;
-
-                while ((end + 1) < string.length)
-                {
-                    if (string[end] == 0 && string[end + 1] == 0)
-                    {
-                        byte[]  nStr = new byte[end - start + 1];
-
-                        System.arraycopy(string, start, nStr, 0, nStr.length);
-
-                        out.writeObject(new DEROctetString(nStr));
-                        start = end + 1;
-                    }
-                    end++;
-                }
-
-                byte[]  nStr = new byte[string.length - start];
-
-                System.arraycopy(string, start, nStr, 0, nStr.length);
-
-                out.writeObject(new DEROctetString(nStr));
+                out.writeObject(e.nextElement());
             }
 
             out.write(0x00);
diff --git a/src/main/java/org/bouncycastle/asn1/BERFactory.java b/src/main/java/org/bouncycastle/asn1/BERFactory.java
new file mode 100644
index 0000000..82f101c
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERFactory.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+class BERFactory
+{
+    static final BERSequence EMPTY_SEQUENCE = new BERSequence();
+    static final BERSet EMPTY_SET = new BERSet();
+
+    static BERSequence createSequence(ASN1EncodableVector v)
+    {
+        return v.size() < 1 ? EMPTY_SEQUENCE : new BERSequence(v);
+    }
+
+    static BERSet createSet(ASN1EncodableVector v)
+    {
+        return v.size() < 1 ? EMPTY_SET : new BERSet(v);
+    }
+
+    static BERSet createSet(ASN1EncodableVector v, boolean needsSorting)
+    {
+        return v.size() < 1 ? EMPTY_SET : new BERSet(v, needsSorting);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERInputStream.java b/src/main/java/org/bouncycastle/asn1/BERInputStream.java
index 088f915..397fc06 100644
--- a/src/main/java/org/bouncycastle/asn1/BERInputStream.java
+++ b/src/main/java/org/bouncycastle/asn1/BERInputStream.java
@@ -12,7 +12,7 @@
 public class BERInputStream
     extends DERInputStream
 {
-    private DERObject END_OF_STREAM = new DERObject()
+    private static final DERObject END_OF_STREAM = new DERObject()
     {
                                         void encode(
                                             DEROutputStream out)
diff --git a/src/main/java/org/bouncycastle/asn1/BERNull.java b/src/main/java/org/bouncycastle/asn1/BERNull.java
deleted file mode 100644
index 697dd4b..0000000
--- a/src/main/java/org/bouncycastle/asn1/BERNull.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * A BER NULL object.
- */
-public class BERNull
-    extends DERNull
-{
-    // BEGIN android-added
-    /** non-null; unique instance of this class */
-    static public final BERNull THE_ONE = new BERNull();
-    // END android-added
-
-    // BEGIN android-changed
-    private BERNull()
-    {
-    }
-    // END android-changed
-
-    void encode(
-        DEROutputStream  out)
-        throws IOException
-    {
-        if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
-        {
-            out.write(NULL);
-        }
-        else
-        {
-            super.encode(out);
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java b/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
new file mode 100644
index 0000000..bc937ad
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.asn1;
+
+import org.bouncycastle.util.io.Streams;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+public class BEROctetStringParser
+    implements ASN1OctetStringParser
+{
+    private ASN1StreamParser _parser;
+
+    BEROctetStringParser(
+        ASN1StreamParser parser)
+    {
+        _parser = parser;
+    }
+
+    /**
+     * @deprecated will be removed
+     */
+    protected BEROctetStringParser(
+        ASN1ObjectParser parser)
+    {
+        _parser = parser._aIn;
+    }
+
+    public InputStream getOctetStream()
+    {
+        return new ConstructedOctetStream(_parser);
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new BERConstructedOctetString(Streams.readAll(getOctetStream()));
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java b/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
new file mode 100644
index 0000000..cd0ca27
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSequenceParser
+    implements ASN1SequenceParser
+{
+    private ASN1StreamParser _parser;
+
+    BERSequenceParser(ASN1StreamParser parser)
+    {
+        this._parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new BERSequence(_parser.readVector());
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERSetParser.java b/src/main/java/org/bouncycastle/asn1/BERSetParser.java
new file mode 100644
index 0000000..ac280d3
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERSetParser.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERSetParser
+    implements ASN1SetParser
+{
+    private ASN1StreamParser _parser;
+
+    BERSetParser(ASN1StreamParser parser)
+    {
+        this._parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new BERSet(_parser.readVector(), false);
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java b/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
index 2e06c40..e09c910 100644
--- a/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
+++ b/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
@@ -4,8 +4,8 @@
 import java.util.Enumeration;
 
 /**
- * BER TaggedObject - in ASN.1 nottation this is any object proceeded by
- * a [n] where n is some number - these are assume to follow the construction
+ * BER TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
  * rules (as with sequences).
  */
 public class BERTaggedObject
@@ -51,17 +51,16 @@
     {
         if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
         {
-            out.write(CONSTRUCTED | TAGGED | tagNo);
+            out.writeTag(CONSTRUCTED | TAGGED, tagNo);
             out.write(0x80);
 
             if (!empty)
             {
                 if (!explicit)
                 {
+                    Enumeration e;
                     if (obj instanceof ASN1OctetString)
                     {
-                        Enumeration  e;
-
                         if (obj instanceof BERConstructedOctetString)
                         {
                             e = ((BERConstructedOctetString)obj).getObjects();
@@ -70,37 +69,26 @@
                         {
                             ASN1OctetString             octs = (ASN1OctetString)obj;
                             BERConstructedOctetString   berO = new BERConstructedOctetString(octs.getOctets());
-
                             e = berO.getObjects();
                         }
-
-                        while (e.hasMoreElements())
-                        {
-                            out.writeObject(e.nextElement());
-                        }
                     }
                     else if (obj instanceof ASN1Sequence)
                     {
-                        Enumeration  e = ((ASN1Sequence)obj).getObjects();
-
-                        while (e.hasMoreElements())
-                        {
-                            out.writeObject(e.nextElement());
-                        }
+                        e = ((ASN1Sequence)obj).getObjects();
                     }
                     else if (obj instanceof ASN1Set)
                     {
-                        Enumeration  e = ((ASN1Set)obj).getObjects();
-
-                        while (e.hasMoreElements())
-                        {
-                            out.writeObject(e.nextElement());
-                        }
+                        e = ((ASN1Set)obj).getObjects();
                     }
                     else
                     {
                         throw new RuntimeException("not implemented: " + obj.getClass().getName());
                     }
+
+                    while (e.hasMoreElements())
+                    {
+                        out.writeObject(e.nextElement());
+                    }
                 }
                 else
                 {
diff --git a/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java b/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
new file mode 100644
index 0000000..ce7318d
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
@@ -0,0 +1,123 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BERTaggedObjectParser
+    implements ASN1TaggedObjectParser
+{
+    private int _baseTag;
+    private int _tagNumber;
+    private InputStream _contentStream;
+
+    private boolean _indefiniteLength;
+
+    protected BERTaggedObjectParser(
+        int         baseTag,
+        int         tagNumber,
+        InputStream contentStream)
+    {
+        _baseTag = baseTag;
+        _tagNumber = tagNumber;
+        _contentStream = contentStream;
+        _indefiniteLength = contentStream instanceof IndefiniteLengthInputStream;
+    }
+
+    public boolean isConstructed()
+    {
+        return (_baseTag & DERTags.CONSTRUCTED) != 0;
+    }
+
+    public int getTagNo()
+    {
+        return _tagNumber;
+    }
+    
+    public DEREncodable getObjectParser(
+        int     tag,
+        boolean isExplicit)
+        throws IOException
+    {
+        if (isExplicit)
+        {
+            return new ASN1StreamParser(_contentStream).readObject();
+        }
+
+        switch (tag)
+        {
+            case DERTags.SET:
+                if (_indefiniteLength)
+                {
+                    return new BERSetParser(new ASN1StreamParser(_contentStream));
+                }
+                else
+                {
+                    return new DERSetParser(new ASN1StreamParser(_contentStream));
+                }
+            case DERTags.SEQUENCE:
+                if (_indefiniteLength)
+                {
+                    return new BERSequenceParser(new ASN1StreamParser(_contentStream));
+                }
+                else
+                {
+                    return new DERSequenceParser(new ASN1StreamParser(_contentStream));
+                }
+            case DERTags.OCTET_STRING:
+                // TODO Is the handling of definite length constructed encodings correct?
+                if (_indefiniteLength || this.isConstructed())
+                {
+                    return new BEROctetStringParser(new ASN1StreamParser(_contentStream));
+                }
+                else
+                {
+                    return new DEROctetStringParser((DefiniteLengthInputStream)_contentStream);
+                }
+        }
+
+        throw new RuntimeException("implicit tagging not implemented");
+    }
+
+    private ASN1EncodableVector rLoadVector(InputStream in)
+    {
+        try
+        {
+            return new ASN1StreamParser(in).readVector();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException(e.getMessage(), e);
+        }
+    }
+
+    public DERObject getDERObject()
+    {
+        if (_indefiniteLength)
+        {
+            ASN1EncodableVector v = rLoadVector(_contentStream);
+
+            return v.size() == 1
+                ?   new BERTaggedObject(true, _tagNumber, v.get(0))
+                :   new BERTaggedObject(false, _tagNumber, BERFactory.createSequence(v));
+        }
+
+        if (this.isConstructed())
+        {
+            ASN1EncodableVector v = rLoadVector(_contentStream);
+
+            return v.size() == 1
+                ?   new DERTaggedObject(true, _tagNumber, v.get(0))
+                :   new DERTaggedObject(false, _tagNumber, DERFactory.createSequence(v));
+        }
+
+        try
+        {
+            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_contentStream;
+            return new DERTaggedObject(false, _tagNumber, new DEROctetString(defIn.toByteArray()));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java b/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
new file mode 100644
index 0000000..045cffd
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+class ConstructedOctetStream
+    extends InputStream
+{
+    private final ASN1StreamParser _parser;
+
+    private boolean                _first = true;
+    private InputStream            _currentStream;
+
+    ConstructedOctetStream(
+        ASN1StreamParser parser)
+    {
+        _parser = parser;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+            if (s == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = s.getOctetStream();
+        }
+
+        int totalRead = 0;
+
+        for (;;)
+        {
+            int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+            if (numRead >= 0)
+            {
+                totalRead += numRead;
+
+                if (totalRead == len)
+                {
+                    return totalRead;
+                }
+            }
+            else
+            {
+                ASN1OctetStringParser aos = (ASN1OctetStringParser)_parser.readObject();
+
+                if (aos == null)
+                {
+                    _currentStream = null;
+                    return totalRead < 1 ? -1 : totalRead;
+                }
+
+                _currentStream = aos.getOctetStream();
+            }
+        }
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+    
+            if (s == null)
+            {
+                return -1;
+            }
+    
+            _first = false;
+            _currentStream = s.getOctetStream();
+        }
+
+        for (;;)
+        {
+            int b = _currentStream.read();
+
+            if (b >= 0)
+            {
+                return b;
+            }
+
+            ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject();
+
+            if (s == null)
+            {
+                _currentStream = null;
+                return -1;
+            }
+
+            _currentStream = s.getOctetStream();
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java b/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
index ad4f9ed..7ca06d6 100644
--- a/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
+++ b/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
@@ -3,41 +3,102 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * Base class for an application specific object
  */
 public class DERApplicationSpecific 
-    extends DERObject
+    extends ASN1Object
 {
-    private int       tag;
-    private byte[]    octets;
-    
-    public DERApplicationSpecific(
-        int        tag,
-        byte[]    octets)
+    private final boolean   isConstructed;
+    private final int       tag;
+    private final byte[]    octets;
+
+    DERApplicationSpecific(
+        boolean isConstructed,
+        int     tag,
+        byte[]  octets)
     {
+        this.isConstructed = isConstructed;
         this.tag = tag;
         this.octets = octets;
     }
-    
+
+    public DERApplicationSpecific(
+        int    tag,
+        byte[] octets)
+    {
+        this(false, tag, octets);
+    }
+
     public DERApplicationSpecific(
         int                  tag, 
         DEREncodable         object) 
         throws IOException 
     {
-        this.tag = tag | DERTags.CONSTRUCTED;
-        
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DEROutputStream dos = new DEROutputStream(baos);
-        
-        dos.writeObject(object);
-        
-        this.octets = baos.toByteArray();
+        this(true, tag, object);
     }
-    
+
+    public DERApplicationSpecific(
+        boolean      explicit,
+        int          tag,
+        DEREncodable object)
+        throws IOException
+    {
+        byte[] data = object.getDERObject().getDEREncoded();
+
+        this.isConstructed = explicit;
+        this.tag = tag;
+
+        if (explicit)
+        {
+            this.octets = data;
+        }
+        else
+        {
+            int lenBytes = getLengthOfLength(data);
+            byte[] tmp = new byte[data.length - lenBytes];
+            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
+            this.octets = tmp;
+        }
+    }
+
+    public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
+    {
+        this.tag = tagNo;
+        this.isConstructed = true;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        for (int i = 0; i != vec.size(); i++)
+        {
+            try
+            {
+                bOut.write(((ASN1Encodable)vec.get(i)).getEncoded());
+            }
+            catch (IOException e)
+            {
+                throw new ASN1ParsingException("malformed object: " + e, e);
+            }
+        }
+        this.octets = bOut.toByteArray();
+    }
+
+    private int getLengthOfLength(byte[] data)
+    {
+        int count = 2;               // TODO: assumes only a 1 byte tag number
+
+        while((data[count - 1] & 0x80) != 0)
+        {
+            count++;
+        }
+
+        return count;
+    }
+
     public boolean isConstructed()
     {
-        return (tag & DERTags.CONSTRUCTED) != 0;
+        return isConstructed;
     }
     
     public byte[] getContents()
@@ -47,64 +108,118 @@
     
     public int getApplicationTag() 
     {
-        return tag & 0x1F;
+        return tag;
     }
-     
+
+    /**
+     * Return the enclosed object assuming explicit tagging.
+     *
+     * @return  the resulting object
+     * @throws IOException if reconstruction fails.
+     */
     public DERObject getObject() 
         throws IOException 
     {
         return new ASN1InputStream(getContents()).readObject();
     }
+
+    /**
+     * Return the enclosed object assuming implicit tagging.
+     *
+     * @param derTagNo the type tag that should be applied to the object's contents.
+     * @return  the resulting object
+     * @throws IOException if reconstruction fails.
+     */
+    public DERObject getObject(int derTagNo)
+        throws IOException
+    {
+        if (derTagNo >= 0x1f)
+        {
+            throw new IOException("unsupported tag number");
+        }
+
+        byte[] orig = this.getEncoded();
+        byte[] tmp = replaceTagNumber(derTagNo, orig);
+
+        if ((orig[0] & DERTags.CONSTRUCTED) != 0)
+        {
+            tmp[0] |= DERTags.CONSTRUCTED;
+        }
+
+        return new ASN1InputStream(tmp).readObject();
+    }
     
     /* (non-Javadoc)
      * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream)
      */
     void encode(DEROutputStream out) throws IOException
     {
-        out.writeEncoded(DERTags.APPLICATION | tag, octets);
+        int classBits = DERTags.APPLICATION;
+        if (isConstructed)
+        {
+            classBits |= DERTags.CONSTRUCTED; 
+        }
+
+        out.writeEncoded(classBits, tag, octets);
     }
     
-    public boolean equals(
-            Object o)
+    boolean asn1Equals(
+        DERObject o)
     {
-        if ((o == null) || !(o instanceof DERApplicationSpecific))
+        if (!(o instanceof DERApplicationSpecific))
         {
             return false;
         }
-        
+
         DERApplicationSpecific other = (DERApplicationSpecific)o;
-        
-        if (tag != other.tag)
-        {
-            return false;
-        }
-        
-        if (octets.length != other.octets.length)
-        {
-            return false;
-        }
-        
-        for (int i = 0; i < octets.length; i++) 
-        {
-            if (octets[i] != other.octets[i])
-            {
-                return false;
-            }
-        }
-        
-        return true;
+
+        return isConstructed == other.isConstructed
+            && tag == other.tag
+            && Arrays.areEqual(octets, other.octets);
     }
-    
+
     public int hashCode()
     {
-        byte[]  b = this.getContents();
-        int     value = 0;
+        return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
+    }
 
-        for (int i = 0; i != b.length; i++)
+    private byte[] replaceTagNumber(int newTag, byte[] input)
+        throws IOException
+    {
+        int tagNo = input[0] & 0x1f;
+        int index = 1;
+        //
+        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+        //
+        if (tagNo == 0x1f)
         {
-            value ^= (b[i] & 0xff) << (i % 4);
+            tagNo = 0;
+
+            int b = input[index++] & 0xff;
+
+            // X.690-0207 8.1.2.4.2
+            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+            if ((b & 0x7f) == 0) // Note: -1 will pass
+            {
+                throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
+            }
+
+            while ((b >= 0) && ((b & 0x80) != 0))
+            {
+                tagNo |= (b & 0x7f);
+                tagNo <<= 7;
+                b = input[index++] & 0xff;
+            }
+
+            tagNo |= (b & 0x7f);
         }
 
-        return value ^ this.getApplicationTag();
+        byte[] tmp = new byte[input.length - index + 1];
+
+        System.arraycopy(input, index, tmp, 1, tmp.length - 1);
+
+        tmp[0] = (byte)newTag;
+
+        return tmp;
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERBMPString.java b/src/main/java/org/bouncycastle/asn1/DERBMPString.java
index 77ed252..1472325 100644
--- a/src/main/java/org/bouncycastle/asn1/DERBMPString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERBMPString.java
@@ -6,7 +6,7 @@
  * DER BMPString object.
  */
 public class DERBMPString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String  string;
@@ -85,13 +85,18 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     public int hashCode()
     {
         return this.getString().hashCode();
     }
 
-    public boolean equals(
-        Object  o)
+    protected boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERBMPString))
         {
diff --git a/src/main/java/org/bouncycastle/asn1/DERBitString.java b/src/main/java/org/bouncycastle/asn1/DERBitString.java
index 367a297..efcdaca 100644
--- a/src/main/java/org/bouncycastle/asn1/DERBitString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERBitString.java
@@ -1,10 +1,12 @@
 package org.bouncycastle.asn1;
 
+import org.bouncycastle.util.Arrays;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 public class DERBitString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
@@ -165,13 +167,7 @@
     {
         try
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(obj);
-            dOut.close();
-
-            this.data = bOut.toByteArray();
+            this.data = obj.getDERObject().getEncoded(ASN1Encodable.DER);
             this.padBits = 0;
         }
         catch (IOException e)
@@ -220,40 +216,21 @@
 
     public int hashCode()
     {
-        int     value = 0;
-
-        for (int i = 0; i != data.length; i++)
-        {
-            value ^= (data[i] & 0xff) << (i % 4);
-        }
-
-        return value;
+        return padBits ^ Arrays.hashCode(data);
     }
-    
-    public boolean equals(
-        Object  o)
+
+    protected boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERBitString))
         {
             return false;
         }
 
-        DERBitString  other = (DERBitString)o;
+        DERBitString other = (DERBitString)o;
 
-        if (data.length != other.data.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != data.length; i++)
-        {
-            if (data[i] != other.data[i])
-            {
-                return false;
-            }
-        }
-
-        return (padBits == other.padBits);
+        return this.padBits == other.padBits
+            && Arrays.areEqual(this.data, other.data);
     }
 
     public String getString()
@@ -275,7 +252,7 @@
         
         for (int i = 0; i != string.length; i++)
         {
-            buf.append(table[(string[i] >>> 4) % 0xf]);
+            buf.append(table[(string[i] >>> 4) & 0xf]);
             buf.append(table[string[i] & 0xf]);
         }
         
diff --git a/src/main/java/org/bouncycastle/asn1/DERBoolean.java b/src/main/java/org/bouncycastle/asn1/DERBoolean.java
index 2aa93f1..5667715 100644
--- a/src/main/java/org/bouncycastle/asn1/DERBoolean.java
+++ b/src/main/java/org/bouncycastle/asn1/DERBoolean.java
@@ -3,7 +3,7 @@
 import java.io.IOException;
 
 public class DERBoolean
-    extends DERObject
+    extends ASN1Object
 {
     // BEGIN android-changed
     private final byte  value;
@@ -108,8 +108,8 @@
         out.writeEncoded(BOOLEAN, bytes);
     }
     
-    public boolean equals(
-        Object  o)
+    protected boolean asn1Equals(
+        DERObject  o)
     {
         if ((o == null) || !(o instanceof DERBoolean))
         {
diff --git a/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java b/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
index 49f3a17..68d63eb 100644
--- a/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
+++ b/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
@@ -9,8 +9,16 @@
  */
 public class DEREncodableVector
 {
-    private Vector  v = new Vector();
+    Vector v = new Vector();
 
+    /**
+     * @deprecated use ASN1EncodableVector instead.
+     */
+    public DEREncodableVector()
+    {
+
+    }
+    
     public void add(
         DEREncodable   obj)
     {
diff --git a/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
index 170fd97..5a9da4c 100644
--- a/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
+++ b/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
@@ -1,10 +1,12 @@
 package org.bouncycastle.asn1;
 
+import org.bouncycastle.util.Arrays;
+
 import java.io.IOException;
 import java.math.BigInteger;
 
 public class DEREnumerated
-    extends DERObject
+    extends ASN1Object
 {
     byte[]      bytes;
 
@@ -80,8 +82,8 @@
         out.writeEncoded(ENUMERATED, bytes);
     }
     
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DEREnumerated))
         {
@@ -90,24 +92,11 @@
 
         DEREnumerated other = (DEREnumerated)o;
 
-        if (bytes.length != other.bytes.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != bytes.length; i++)
-        {
-            if (bytes[i] != other.bytes[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return Arrays.areEqual(this.bytes, other.bytes);
     }
-    
+
     public int hashCode()
     {
-        return this.getValue().hashCode();
+        return Arrays.hashCode(bytes);
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERExternal.java b/src/main/java/org/bouncycastle/asn1/DERExternal.java
new file mode 100644
index 0000000..ad062c8
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DERExternal.java
@@ -0,0 +1,267 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Class representing the DER-type External
+ */
+public class DERExternal
+    extends ASN1Object
+{
+    private DERObjectIdentifier directReference;
+    private DERInteger indirectReference;
+    private ASN1Object dataValueDescriptor;
+    private int encoding;
+    private DERObject externalContent;
+    
+    public DERExternal(ASN1EncodableVector vector)
+    {
+        int offset = 0;
+        DERObject enc = vector.get(offset).getDERObject();
+        if (enc instanceof DERObjectIdentifier)
+        {
+            directReference = (DERObjectIdentifier)enc;
+            offset++;
+            enc = vector.get(offset).getDERObject();
+        }
+        if (enc instanceof DERInteger)
+        {
+            indirectReference = (DERInteger) enc;
+            offset++;
+            enc = vector.get(offset).getDERObject();
+        }
+        if (!(enc instanceof DERTaggedObject))
+        {
+            dataValueDescriptor = (ASN1Object) enc;
+            offset++;
+            enc = vector.get(offset).getDERObject();
+        }
+        if (!(enc instanceof DERTaggedObject))
+        {
+            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+        }
+        DERTaggedObject obj = (DERTaggedObject)enc;
+        setEncoding(obj.getTagNo());
+        externalContent = obj.getObject();
+    }
+
+    /**
+     * Creates a new instance of DERExternal
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param externalData The external data in its encoded form.
+     */
+    public DERExternal(DERObjectIdentifier directReference, DERInteger indirectReference, ASN1Object dataValueDescriptor, DERTaggedObject externalData)
+    {
+        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.getDERObject());
+    }
+
+    /**
+     * Creates a new instance of DERExternal.
+     * See X.690 for more informations about the meaning of these parameters
+     * @param directReference The direct reference or <code>null</code> if not set.
+     * @param indirectReference The indirect reference or <code>null</code> if not set.
+     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
+     * @param encoding The encoding to be used for the external data
+     * @param externalData The external data
+     */
+    public DERExternal(DERObjectIdentifier directReference, DERInteger indirectReference, ASN1Object dataValueDescriptor, int encoding, DERObject externalData)
+    {
+        setDirectReference(directReference);
+        setIndirectReference(indirectReference);
+        setDataValueDescriptor(dataValueDescriptor);
+        setEncoding(encoding);
+        setExternalContent(externalData.getDERObject());
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode()
+    {
+        int ret = 0;
+        if (directReference != null)
+        {
+            ret = directReference.hashCode();
+        }
+        if (indirectReference != null)
+        {
+            ret ^= indirectReference.hashCode();
+        }
+        if (dataValueDescriptor != null)
+        {
+            ret ^= dataValueDescriptor.hashCode();
+        }
+        ret ^= externalContent.hashCode();
+        return ret;
+    }
+
+    /* (non-Javadoc)
+     * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream)
+     */
+    void encode(DEROutputStream out)
+        throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if (directReference != null)
+        {
+            baos.write(directReference.getDEREncoded());
+        }
+        if (indirectReference != null)
+        {
+            baos.write(indirectReference.getDEREncoded());
+        }
+        if (dataValueDescriptor != null)
+        {
+            baos.write(dataValueDescriptor.getDEREncoded());
+        }
+        DERTaggedObject obj = new DERTaggedObject(encoding, externalContent);
+        baos.write(obj.getDEREncoded());
+        out.writeEncoded(DERTags.CONSTRUCTED, DERTags.EXTERNAL, baos.toByteArray());
+    }
+
+    /* (non-Javadoc)
+     * @see org.bouncycastle.asn1.ASN1Object#asn1Equals(org.bouncycastle.asn1.DERObject)
+     */
+    boolean asn1Equals(DERObject o)
+    {
+        if (!(o instanceof DERExternal))
+        {
+            return false;
+        }
+        if (this == o)
+        {
+            return true;
+        }
+        DERExternal other = (DERExternal)o;
+        if (directReference != null)
+        {
+            if (other.directReference == null || !other.directReference.equals(directReference))  
+            {
+                return false;
+            }
+        }
+        if (indirectReference != null)
+        {
+            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
+            {
+                return false;
+            }
+        }
+        if (dataValueDescriptor != null)
+        {
+            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
+            {
+                return false;
+            }
+        }
+        return externalContent.equals(other.externalContent);
+    }
+
+    /**
+     * Returns the data value descriptor
+     * @return The descriptor
+     */
+    public ASN1Object getDataValueDescriptor()
+    {
+        return dataValueDescriptor;
+    }
+
+    /**
+     * Returns the direct reference of the external element
+     * @return The reference
+     */
+    public DERObjectIdentifier getDirectReference()
+    {
+        return directReference;
+    }
+
+    /**
+     * Returns the encoding of the content. Valid values are
+     * <ul>
+     * <li><code>0</code> single-ASN1-type</li>
+     * <li><code>1</code> OCTET STRING</li>
+     * <li><code>2</code> BIT STRING</li>
+     * </ul>
+     * @return The encoding
+     */
+    public int getEncoding()
+    {
+        return encoding;
+    }
+    
+    /**
+     * Returns the content of this element
+     * @return The content
+     */
+    public DERObject getExternalContent()
+    {
+        return externalContent;
+    }
+    
+    /**
+     * Returns the indirect reference of this element
+     * @return The reference
+     */
+    public DERInteger getIndirectReference()
+    {
+        return indirectReference;
+    }
+    
+    /**
+     * Sets the data value descriptor
+     * @param dataValueDescriptor The descriptor
+     */
+    private void setDataValueDescriptor(ASN1Object dataValueDescriptor)
+    {
+        this.dataValueDescriptor = dataValueDescriptor;
+    }
+
+    /**
+     * Sets the direct reference of the external element
+     * @param directReferemce The reference
+     */
+    private void setDirectReference(DERObjectIdentifier directReferemce)
+    {
+        this.directReference = directReferemce;
+    }
+    
+    /**
+     * Sets the encoding of the content. Valid values are
+     * <ul>
+     * <li><code>0</code> single-ASN1-type</li>
+     * <li><code>1</code> OCTET STRING</li>
+     * <li><code>2</code> BIT STRING</li>
+     * </ul>
+     * @param encoding The encoding
+     */
+    private void setEncoding(int encoding)
+    {
+        if (encoding < 0 || encoding > 2)
+        {
+            throw new IllegalArgumentException("invalid encoding value: " + encoding);
+        }
+        this.encoding = encoding;
+    }
+    
+    /**
+     * Sets the content of this element
+     * @param externalContent The content
+     */
+    private void setExternalContent(DERObject externalContent)
+    {
+        this.externalContent = externalContent;
+    }
+    
+    /**
+     * Sets the indirect reference of this element
+     * @param indirectReference The reference
+     */
+    private void setIndirectReference(DERInteger indirectReference)
+    {
+        this.indirectReference = indirectReference;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERExternalParser.java b/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
new file mode 100644
index 0000000..0fbfb68
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERExternalParser
+    implements DEREncodable
+{
+    private ASN1StreamParser _parser;
+
+    /**
+     * 
+     */
+    public DERExternalParser(ASN1StreamParser parser)
+    {
+        this._parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return _parser.readObject();
+    }
+    
+    public DERObject getDERObject()
+    {
+        try 
+        {
+            return new DERExternal(_parser.readVector());
+        }
+        catch (IOException ioe) 
+        {
+            throw new ASN1ParsingException("unable to get DER object", ioe);
+        }
+        catch (IllegalArgumentException ioe) 
+        {
+            throw new ASN1ParsingException("unable to get DER object", ioe);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERFactory.java b/src/main/java/org/bouncycastle/asn1/DERFactory.java
new file mode 100644
index 0000000..7364282
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DERFactory.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1;
+
+class DERFactory
+{
+    static final DERSequence EMPTY_SEQUENCE = new DERSequence();
+    static final DERSet EMPTY_SET = new DERSet();
+
+    static DERSequence createSequence(ASN1EncodableVector v)
+    {
+        return v.size() < 1 ? EMPTY_SEQUENCE : new DERSequence(v);
+    }
+
+    static DERSet createSet(ASN1EncodableVector v)
+    {
+        return v.size() < 1 ? EMPTY_SET : new DERSet(v);
+    }
+
+    static DERSet createSet(ASN1EncodableVector v, boolean needsSorting)
+    {
+        return v.size() < 1 ? EMPTY_SET : new DERSet(v, needsSorting);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERGeneralString.java b/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
index c93fe96..1992cf3 100644
--- a/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
@@ -3,7 +3,7 @@
 import java.io.IOException;
 
 public class DERGeneralString 
-    extends DERObject implements DERString
+    extends ASN1Object implements DERString
 {
     private String string;
 
@@ -52,7 +52,12 @@
     {
         return string;
     }
-    
+
+    public String toString()
+    {
+        return string;
+    }
+
     public byte[] getOctets() 
     {
         char[] cs = string.toCharArray();
@@ -75,7 +80,7 @@
         return this.getString().hashCode();
     }
     
-    public boolean equals(Object o) 
+    boolean asn1Equals(DERObject o)
     {
         if (!(o instanceof DERGeneralString)) 
         {
diff --git a/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java b/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
index c70574b..5366347 100644
--- a/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
+++ b/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -5,12 +5,13 @@
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.SimpleTimeZone;
+import java.util.TimeZone;
 
 /**
  * Generalized time object.
  */
 public class DERGeneralizedTime
-    extends DERObject
+    extends ASN1Object
 {
     String      time;
 
@@ -52,17 +53,26 @@
     }
     
     /**
-     * The correct format for this is YYYYMMDDHHMMSSZ, or without the Z
+     * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
      * for local time, or Z+-HHMM on the end, for difference between local
-     * time and UTC time.
-     * <p>
+     * time and UTC time. The fractional second amount f must consist of at
+     * least one number with trailing zeroes removed.
      *
      * @param time the time string.
+     * @exception IllegalArgumentException if String is an illegal format.
      */
     public DERGeneralizedTime(
         String  time)
     {
         this.time = time;
+        try
+        {
+            this.getDate();
+        }
+        catch (ParseException e)
+        {
+            throw new IllegalArgumentException("invalid date string: " + e.getMessage());
+        }
     }
 
     /**
@@ -95,6 +105,15 @@
     }
 
     /**
+     * Return the time.
+     * @return The time string as it appeared in the encoded object.
+     */
+    public String getTimeString()
+    {
+        return time;
+    }
+    
+    /**
      * return the time - always in the form of 
      *  YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
      * <p>
@@ -140,29 +159,122 @@
                 }
             }
         }            
-
-        return time;
+        return time + calculateGMTOffset();
     }
 
-    public Date getDate() 
+    private String calculateGMTOffset()
+    {
+        String sign = "+";
+        TimeZone timeZone = TimeZone.getDefault();
+        int offset = timeZone.getRawOffset();
+        if (offset < 0)
+        {
+            sign = "-";
+            offset = -offset;
+        }
+        int hours = offset / (60 * 60 * 1000);
+        int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000);
+
+        try
+        {
+            if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate()))
+            {
+                hours += sign.equals("+") ? 1 : -1;
+            }
+        }
+        catch (ParseException e)
+        {
+            // we'll do our best and ignore daylight savings
+        }
+
+        return "GMT" + sign + convert(hours) + ":" + convert(minutes);
+    }
+
+    private String convert(int time)
+    {
+        if (time < 10)
+        {
+            return "0" + time;
+        }
+
+        return Integer.toString(time);
+    }
+
+    public Date getDate()
         throws ParseException
     {
         SimpleDateFormat dateF;
+        String d = time;
 
-        if (time.indexOf('.') == 14)
+        if (time.endsWith("Z"))
         {
-            dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
+            if (hasFractionalSeconds())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+            }
+
+            dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+        }
+        else if (time.indexOf('-') > 0 || time.indexOf('+') > 0)
+        {
+            d = this.getTime();
+            if (hasFractionalSeconds())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+            }
+
+            dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
         }
         else
         {
-            dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+            if (hasFractionalSeconds())
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
+            }
+            else
+            {
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+            }
+
+            dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
         }
-        
-        dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
-        
-        return dateF.parse(time);
+
+        if (hasFractionalSeconds())
+        {
+            // java misinterprets extra digits as being milliseconds...
+            String frac = d.substring(14);
+            int    index;
+            for (index = 1; index < frac.length(); index++)
+            {
+                char ch = frac.charAt(index);
+                if (!('0' <= ch && ch <= '9'))
+                {
+                    break;        
+                }
+            }
+            if (index - 1 > 3)
+            {
+                frac = frac.substring(0, 4) + frac.substring(index);
+                d = d.substring(0, 14) + frac;
+            }
+        }
+
+        return dateF.parse(d);
     }
-    
+
+    private boolean hasFractionalSeconds()
+    {
+        return time.indexOf('.') == 14;
+    }
+
     private byte[] getOctets()
     {
         char[]  cs = time.toCharArray();
@@ -184,10 +296,10 @@
         out.writeEncoded(GENERALIZED_TIME, this.getOctets());
     }
     
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERGeneralizedTime))
+        if (!(o instanceof DERGeneralizedTime))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/DERIA5String.java b/src/main/java/org/bouncycastle/asn1/DERIA5String.java
index a75ab1e..a90830c 100644
--- a/src/main/java/org/bouncycastle/asn1/DERIA5String.java
+++ b/src/main/java/org/bouncycastle/asn1/DERIA5String.java
@@ -6,7 +6,7 @@
  * DER IA5String object - this is an ascii string.
  */
 public class DERIA5String
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String  string;
@@ -70,11 +70,35 @@
     }
 
     /**
-     * basic constructor - with string.
+     * basic constructor - without validation.
      */
     public DERIA5String(
         String   string)
     {
+        this(string, false);
+    }
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in an IA5String.
+     */
+    public DERIA5String(
+        String   string,
+        boolean  validate)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("string cannot be null");
+        }
+        if (validate && !isIA5String(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
         this.string = string;
     }
 
@@ -83,6 +107,11 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     public byte[] getOctets()
     {
         char[]  cs = string.toCharArray();
@@ -108,8 +137,8 @@
         return this.getString().hashCode();
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERIA5String))
         {
@@ -120,4 +149,26 @@
 
         return this.getString().equals(s.getString());
     }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as an IA5String, false otherwise.
+     *
+     * @return true if in printable set, false otherwise.
+     */
+    public static boolean isIA5String(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERInputStream.java b/src/main/java/org/bouncycastle/asn1/DERInputStream.java
index 37763e4..6edc699 100644
--- a/src/main/java/org/bouncycastle/asn1/DERInputStream.java
+++ b/src/main/java/org/bouncycastle/asn1/DERInputStream.java
@@ -62,7 +62,7 @@
             
             if (length < 0)
             {
-                throw new IOException("corrupted steam - negative length found");
+                throw new IOException("corrupted stream - negative length found");
             }
         }
 
@@ -198,7 +198,7 @@
                     if ((tag & CONSTRUCTED) == 0)
                     {
                         // BEGIN android-changed
-                        return new DERTaggedObject(false, tag & 0x1f, DERNull.THE_ONE);
+                        return new DERTaggedObject(false, tag & 0x1f, DERNull.INSTANCE);
                         // END android-changed
                     }
                     else
diff --git a/src/main/java/org/bouncycastle/asn1/DERInteger.java b/src/main/java/org/bouncycastle/asn1/DERInteger.java
index e80d895..8f97428 100644
--- a/src/main/java/org/bouncycastle/asn1/DERInteger.java
+++ b/src/main/java/org/bouncycastle/asn1/DERInteger.java
@@ -3,8 +3,10 @@
 import java.io.IOException;
 import java.math.BigInteger;
 
+import org.bouncycastle.util.Arrays;
+
 public class DERInteger
-    extends DERObject
+    extends ASN1Object
 {
     byte[]      bytes;
 
@@ -101,8 +103,8 @@
          return value;
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERInteger))
         {
@@ -111,20 +113,7 @@
 
         DERInteger other = (DERInteger)o;
 
-        if (bytes.length != other.bytes.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != bytes.length; i++)
-        {
-            if (bytes[i] != other.bytes[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return Arrays.areEqual(bytes, other.bytes);
     }
 
     public String toString()
diff --git a/src/main/java/org/bouncycastle/asn1/DERNull.java b/src/main/java/org/bouncycastle/asn1/DERNull.java
index 2be5c80..c945f5c 100644
--- a/src/main/java/org/bouncycastle/asn1/DERNull.java
+++ b/src/main/java/org/bouncycastle/asn1/DERNull.java
@@ -8,18 +8,17 @@
 public class DERNull
     extends ASN1Null
 {
-    // BEGIN android-added
-    /** non-null; unique instance of this class */
-    static public final DERNull THE_ONE = new DERNull();
-    // END android-added
+    public static final DERNull INSTANCE = new DERNull();
 
     // BEGIN android-changed
     private static final byte[]  zeroBytes = new byte[0];
+    // END android-changed
 
-    /*package*/ DERNull()
+    // BEGIN android-changed
+    protected DERNull()
+    // END android-changed
     {
     }
-    // END android-changed
 
     void encode(
         DEROutputStream  out)
@@ -27,20 +26,4 @@
     {
         out.writeEncoded(NULL, zeroBytes);
     }
-    
-    public boolean equals(
-        Object o)
-    {
-        if ((o == null) || !(o instanceof DERNull))
-        {
-            return false;
-        }
-        
-        return true;
-    }
-    
-    public int hashCode()
-    {
-        return 0;
-    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERNumericString.java b/src/main/java/org/bouncycastle/asn1/DERNumericString.java
index b53716a..3c72193 100644
--- a/src/main/java/org/bouncycastle/asn1/DERNumericString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERNumericString.java
@@ -6,7 +6,7 @@
  * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
  */
 public class DERNumericString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String  string;
@@ -70,11 +70,31 @@
     }
 
     /**
-     * basic constructor - with string.
+     * basic constructor -  without validation..
      */
     public DERNumericString(
         String   string)
     {
+        this(string, false);
+    }
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a NumericString.
+     */
+    public DERNumericString(
+        String   string,
+        boolean  validate)
+    {
+        if (validate && !isNumericString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
         this.string = string;
     }
 
@@ -83,6 +103,11 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     public byte[] getOctets()
     {
         char[]  cs = string.toCharArray();
@@ -108,8 +133,8 @@
         return this.getString().hashCode();
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERNumericString))
         {
@@ -120,4 +145,33 @@
 
         return this.getString().equals(s.getString());
     }
+
+    /**
+     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
+     *
+     * @param str string to validate.
+     * @return true if numeric, fale otherwise.
+     */
+    public static boolean isNumericString(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if (('0' <= ch && ch <= '9') || ch == ' ')
+            {
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
index 190d727..bad9473 100644
--- a/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
+++ b/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
@@ -6,7 +6,7 @@
 import java.math.BigInteger;
 
 public class DERObjectIdentifier
-    extends DERObject
+    extends ASN1Object
 {
     String      identifier;
 
@@ -244,10 +244,10 @@
         return identifier.hashCode();
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERObjectIdentifier))
+        if (!(o instanceof DERObjectIdentifier))
         {
             return false;
         }
@@ -263,8 +263,20 @@
     private static boolean isValidIdentifier(
         String identifier)
     {
+        if (identifier.length() < 3
+            || identifier.charAt(1) != '.')
+        {
+            return false;
+        }
+
+        char first = identifier.charAt(0);
+        if (first < '0' || first > '2')
+        {
+            return false;
+        }
+
         boolean periodAllowed = false;
-        for (int i = identifier.length() - 1; i >= 0; i--)
+        for (int i = identifier.length() - 1; i >= 2; i--)
         {
             char ch = identifier.charAt(i);
 
diff --git a/src/main/java/org/bouncycastle/asn1/DEROctetString.java b/src/main/java/org/bouncycastle/asn1/DEROctetString.java
index bf7a86b..cadd60c 100644
--- a/src/main/java/org/bouncycastle/asn1/DEROctetString.java
+++ b/src/main/java/org/bouncycastle/asn1/DEROctetString.java
@@ -26,4 +26,12 @@
     {
         out.writeEncoded(OCTET_STRING, string);
     }
+
+    static void encode(
+        DEROutputStream derOut,
+        byte[]          bytes)
+        throws IOException
+    {
+        derOut.writeEncoded(DERTags.OCTET_STRING, bytes);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java b/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
new file mode 100644
index 0000000..f6138d9
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+public class DEROctetStringParser
+    implements ASN1OctetStringParser
+{
+    private DefiniteLengthInputStream stream;
+
+    DEROctetStringParser(
+        DefiniteLengthInputStream stream)
+    {
+        this.stream = stream;
+    }
+
+    public InputStream getOctetStream()
+    {
+        return stream;
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new DEROctetString(stream.toByteArray());
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DEROutputStream.java b/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
index f55142c..b78f7ca 100644
--- a/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
+++ b/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
@@ -50,6 +50,47 @@
         write(bytes);
     }
 
+    void writeTag(int flags, int tagNo)
+        throws IOException
+    {
+        if (tagNo < 31)
+        {
+            write(flags | tagNo);
+        }
+        else
+        {
+            write(flags | 0x1f);
+            if (tagNo < 128)
+            {
+                write(tagNo);
+            }
+            else
+            {
+                byte[] stack = new byte[5];
+                int pos = stack.length;
+
+                stack[--pos] = (byte)(tagNo & 0x7F);
+
+                do
+                {
+                    tagNo >>= 7;
+                    stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
+                }
+                while (tagNo > 127);
+
+                write(stack, pos, stack.length - pos);
+            }
+        }
+    }
+
+    void writeEncoded(int flags, int tagNo, byte[] bytes)
+        throws IOException
+    {
+        writeTag(flags, tagNo);
+        writeLength(bytes.length);
+        write(bytes);
+    }
+
     protected void writeNull()
         throws IOException
     {
@@ -57,6 +98,18 @@
         write(0x00);
     }
 
+    public void write(byte[] buf)
+        throws IOException
+    {
+        out.write(buf, 0, buf.length);
+    }
+
+    public void write(byte[] buf, int offSet, int len)
+        throws IOException
+    {
+        out.write(buf, offSet, len);
+    }
+
     public void writeObject(
         Object    obj)
         throws IOException
diff --git a/src/main/java/org/bouncycastle/asn1/DERPrintableString.java b/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
index 00aec9b..c7a6f44 100644
--- a/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
@@ -6,7 +6,7 @@
  * DER PrintableString object.
  */
 public class DERPrintableString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     // BEGIN android-changed
@@ -74,11 +74,31 @@
     }
 
     /**
-     * basic constructor
+     * basic constructor - this does not validate the string
      */
     public DERPrintableString(
         String   string)
     {
+        this(string, false);
+    }
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a PrintableString.
+     */
+    public DERPrintableString(
+        String   string,
+        boolean  validate)
+    {
+        if (validate && !isPrintableString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
         // BEGIN android-changed
         this.string = string.intern();
         // END android-changed
@@ -114,8 +134,8 @@
         return this.getString().hashCode();
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
         if (!(o instanceof DERPrintableString))
         {
@@ -129,6 +149,62 @@
 
     public String toString()
     {
-      return string;
+        return string;
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as a PrintableString, false otherwise.
+     *
+     * @return true if in printable set, false otherwise.
+     */
+    public static boolean isPrintableString(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if ('a' <= ch && ch <= 'z')
+            {
+                continue;
+            }
+
+            if ('A' <= ch && ch <= 'Z')
+            {
+                continue;
+            }
+
+            if ('0' <= ch && ch <= '9')
+            {
+                continue;
+            }
+
+            switch (ch)
+            {
+            case ' ':
+            case '\'':
+            case '(':
+            case ')':
+            case '+':
+            case '-':
+            case '.':
+            case ':':
+            case '=':
+            case '?':
+            case '/':
+            case ',':
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERSequence.java b/src/main/java/org/bouncycastle/asn1/DERSequence.java
index 1cfd38d..a2e6ab5 100644
--- a/src/main/java/org/bouncycastle/asn1/DERSequence.java
+++ b/src/main/java/org/bouncycastle/asn1/DERSequence.java
@@ -59,6 +59,7 @@
         DEROutputStream out)
         throws IOException
     {
+        // TODO Intermediate buffer could be avoided if we could calculate expected length
         ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
         DEROutputStream         dOut = new DEROutputStream(bOut);
         Enumeration             e = this.getObjects();
diff --git a/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java b/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
new file mode 100644
index 0000000..59ba7f7
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERSequenceParser
+    implements ASN1SequenceParser
+{
+    private ASN1StreamParser _parser;
+
+    DERSequenceParser(ASN1StreamParser parser)
+    {
+        this._parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new DERSequence(_parser.readVector());
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERSet.java b/src/main/java/org/bouncycastle/asn1/DERSet.java
index e1aa1d5..b116e0c 100644
--- a/src/main/java/org/bouncycastle/asn1/DERSet.java
+++ b/src/main/java/org/bouncycastle/asn1/DERSet.java
@@ -79,6 +79,7 @@
         DEROutputStream out)
         throws IOException
     {
+        // TODO Intermediate buffer could be avoided if we could calculate expected length
         ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
         DEROutputStream         dOut = new DEROutputStream(bOut);
         Enumeration             e = this.getObjects();
diff --git a/src/main/java/org/bouncycastle/asn1/DERSetParser.java b/src/main/java/org/bouncycastle/asn1/DERSetParser.java
new file mode 100644
index 0000000..2793e51
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DERSetParser.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class DERSetParser
+    implements ASN1SetParser
+{
+    private ASN1StreamParser _parser;
+
+    DERSetParser(ASN1StreamParser parser)
+    {
+        this._parser = parser;
+    }
+
+    public DEREncodable readObject()
+        throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public DERObject getDERObject()
+    {
+        try
+        {
+            return new DERSet(_parser.readVector(), false);
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/DERT61String.java b/src/main/java/org/bouncycastle/asn1/DERT61String.java
index 8c7c99a..09039fc 100644
--- a/src/main/java/org/bouncycastle/asn1/DERT61String.java
+++ b/src/main/java/org/bouncycastle/asn1/DERT61String.java
@@ -6,7 +6,7 @@
  * DER T61String (also the teletex string)
  */
 public class DERT61String
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String  string;
@@ -83,6 +83,11 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     void encode(
         DEROutputStream  out)
         throws IOException
@@ -103,10 +108,10 @@
         return bs; 
     }
 
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERT61String))
+        if (!(o instanceof DERT61String))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java b/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
index d42f0d6..a1d3687 100644
--- a/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
+++ b/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
@@ -1,16 +1,17 @@
 package org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 /**
- * DER TaggedObject - in ASN.1 nottation this is any object proceeded by
- * a [n] where n is some number - these are assume to follow the construction
+ * DER TaggedObject - in ASN.1 notation this is any object preceded by
+ * a [n] where n is some number - these are assumed to follow the construction
  * rules (as with sequences).
  */
 public class DERTaggedObject
     extends ASN1TaggedObject
 {
+    private static final byte[] ZERO_BYTES = new byte[0];
+
     /**
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
@@ -51,38 +52,34 @@
     {
         if (!empty)
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(obj);
-            dOut.close();
-
-            byte[]  bytes = bOut.toByteArray();
+            byte[] bytes = obj.getDERObject().getEncoded(DER);
 
             if (explicit)
             {
-                out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, bytes);
+                out.writeEncoded(CONSTRUCTED | TAGGED, tagNo, bytes);
             }
             else
             {
                 //
                 // need to mark constructed types...
                 //
+                int flags;
                 if ((bytes[0] & CONSTRUCTED) != 0)
                 {
-                    bytes[0] = (byte)(CONSTRUCTED | TAGGED | tagNo);
+                    flags = CONSTRUCTED | TAGGED;
                 }
                 else
                 {
-                    bytes[0] = (byte)(TAGGED | tagNo);
+                    flags = TAGGED;
                 }
 
-                out.write(bytes);
+                out.writeTag(flags, tagNo);
+                out.write(bytes, 1, bytes.length - 1);
             }
         }
         else
         {
-            out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, new byte[0]);
+            out.writeEncoded(CONSTRUCTED | TAGGED, tagNo, ZERO_BYTES);
         }
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERUTCTime.java b/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
index a6d0cc7..7a05664 100644
--- a/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
+++ b/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.asn1;
 
 import java.io.IOException;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.SimpleTimeZone;
@@ -9,7 +10,7 @@
  * UTC time object.
  */
 public class DERUTCTime
-    extends DERObject
+    extends ASN1Object
 {
     String      time;
 
@@ -64,6 +65,14 @@
         String  time)
     {
         this.time = time;
+        try
+        {
+            this.getDate();
+        }
+        catch (ParseException e)
+        {
+            throw new IllegalArgumentException("invalid date string: " + e.getMessage());
+        }
     }
 
     /**
@@ -96,6 +105,38 @@
     }
 
     /**
+     * return the time as a date based on whatever a 2 digit year will return. For
+     * standardised processing use getAdjustedDate().
+     *
+     * @return the resulting date
+     * @exception ParseException if the date string cannot be parsed.
+     */
+    public Date getDate()
+        throws ParseException
+    {
+        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
+
+        return dateF.parse(getTime());
+    }
+
+    /**
+     * return the time as an adjusted date
+     * in the range of 1950 - 2049.
+     *
+     * @return a date in the range of 1950 to 2049.
+     * @exception ParseException if the date string cannot be parsed.
+     */
+    public Date getAdjustedDate()
+        throws ParseException
+    {
+        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+
+        dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+        return dateF.parse(getAdjustedTime());
+    }
+
+    /**
      * return the time - always in the form of 
      *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
      * <p>
@@ -116,24 +157,44 @@
         //
         // standardise the format.
         //
-        if (time.length() == 11)
+        if (time.indexOf('-') < 0 && time.indexOf('+') < 0)
         {
-            return time.substring(0, 10) + "00GMT+00:00";
+            if (time.length() == 11)
+            {
+                return time.substring(0, 10) + "00GMT+00:00";
+            }
+            else
+            {
+                return time.substring(0, 12) + "GMT+00:00";
+            }
         }
-        else if (time.length() == 13)
+        else
         {
-            return time.substring(0, 12) + "GMT+00:00";
-        }
-        else if (time.length() == 17)
-        {
-            return time.substring(0, 12) + "GMT" + time.substring(12, 15) + ":" + time.substring(15, 17);
-        }
+            int index = time.indexOf('-');
+            if (index < 0)
+            {
+                index = time.indexOf('+');
+            }
+            String d = time;
 
-        return time;
+            if (index == time.length() - 3)
+            {
+                d += "00";
+            }
+
+            if (index == 10)
+            {
+                return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15);
+            }
+            else
+            {
+                return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" +  d.substring(15, 17);
+            }
+        }
     }
 
     /**
-     * return the time as an adjusted date with a 4 digit year. This goes
+     * return a time string as an adjusted date with a 4 digit year. This goes
      * in the range of 1950 - 2049.
      */
     public String getAdjustedTime()
@@ -170,10 +231,10 @@
         out.writeEncoded(UTC_TIME, this.getOctets());
     }
     
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERUTCTime))
+        if (!(o instanceof DERUTCTime))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/DERUTF8String.java b/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
index 1402d38..082aa63 100644
--- a/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
+++ b/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
@@ -1,14 +1,14 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
 import org.bouncycastle.util.Strings;
 
+import java.io.IOException;
+
 /**
  * DER UTF8String object.
  */
 public class DERUTF8String
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String string;
@@ -79,12 +79,17 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     public int hashCode()
     {
         return this.getString().hashCode();
     }
 
-    public boolean equals(Object o)
+    boolean asn1Equals(DERObject o)
     {
         if (!(o instanceof DERUTF8String))
         {
diff --git a/src/main/java/org/bouncycastle/asn1/DERUniversalString.java b/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
index ec8c519..68be9a0 100644
--- a/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
@@ -7,7 +7,7 @@
  * DER UniversalString object.
  */
 public class DERUniversalString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
@@ -78,13 +78,18 @@
         
         for (int i = 0; i != string.length; i++)
         {
-            buf.append(table[(string[i] >>> 4) % 0xf]);
+            buf.append(table[(string[i] >>> 4) & 0xf]);
             buf.append(table[string[i] & 0xf]);
         }
         
         return buf.toString();
     }
 
+    public String toString()
+    {
+        return getString();
+    }
+
     public byte[] getOctets()
     {
         return string;
@@ -97,10 +102,10 @@
         out.writeEncoded(UNIVERSAL_STRING, this.getOctets());
     }
     
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERUniversalString))
+        if (!(o instanceof DERUniversalString))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/DERUnknownTag.java b/src/main/java/org/bouncycastle/asn1/DERUnknownTag.java
index 8b0631c..1feed40 100644
--- a/src/main/java/org/bouncycastle/asn1/DERUnknownTag.java
+++ b/src/main/java/org/bouncycastle/asn1/DERUnknownTag.java
@@ -2,27 +2,44 @@
 
 import java.io.IOException;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * We insert one of these when we find a tag we don't recognise.
  */
 public class DERUnknownTag
     extends DERObject
 {
-    int         tag;
-    byte[]      data;
+    private boolean   isConstructed;
+    private int       tag;
+    private byte[]    data;
 
     /**
      * @param tag the tag value.
-     * @param data the octets making up the time.
+     * @param data the contents octets.
      */
     public DERUnknownTag(
         int     tag,
         byte[]  data)
     {
+        this(false, tag, data);
+    }
+
+    public DERUnknownTag(
+        boolean isConstructed,
+        int     tag,
+        byte[]  data)
+    {
+        this.isConstructed = isConstructed;
         this.tag = tag;
         this.data = data;
     }
 
+    public boolean isConstructed()
+    {
+        return isConstructed;
+    }
+
     public int getTag()
     {
         return tag;
@@ -37,50 +54,26 @@
         DEROutputStream  out)
         throws IOException
     {
-        out.writeEncoded(tag, data);
+        out.writeEncoded(isConstructed ? DERTags.CONSTRUCTED : 0, tag, data);
     }
     
     public boolean equals(
         Object o)
     {
-        if ((o == null) || !(o instanceof DERUnknownTag))
+        if (!(o instanceof DERUnknownTag))
         {
             return false;
         }
         
         DERUnknownTag other = (DERUnknownTag)o;
-        
-        if (tag != other.tag)
-        {
-            return false;
-        }
-        
-        if (data.length != other.data.length)
-        {
-            return false;
-        }
-        
-        for (int i = 0; i < data.length; i++) 
-        {
-            if(data[i] != other.data[i])
-            {
-                return false;
-            }
-        }
-        
-        return true;
+
+        return isConstructed == other.isConstructed
+            && tag == other.tag
+            && Arrays.areEqual(data, other.data);
     }
     
     public int hashCode()
     {
-        byte[]  b = this.getData();
-        int     value = 0;
-
-        for (int i = 0; i != b.length; i++)
-        {
-            value ^= (b[i] & 0xff) << (i % 4);
-        }
-
-        return value ^ this.getTag();
+        return (isConstructed ? ~0 : 0) ^ tag ^ Arrays.hashCode(data);
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/DERVisibleString.java b/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
index 1660eb2..9d0c991 100644
--- a/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
+++ b/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
@@ -6,7 +6,7 @@
  * DER VisibleString object.
  */
 public class DERVisibleString
-    extends DERObject
+    extends ASN1Object
     implements DERString
 {
     String  string;
@@ -83,6 +83,11 @@
         return string;
     }
 
+    public String toString()
+    {
+        return string;
+    }
+
     public byte[] getOctets()
     {
         char[]  cs = string.toCharArray();
@@ -103,10 +108,10 @@
         out.writeEncoded(VISIBLE_STRING, this.getOctets());
     }
     
-    public boolean equals(
-        Object  o)
+    boolean asn1Equals(
+        DERObject  o)
     {
-        if ((o == null) || !(o instanceof DERVisibleString))
+        if (!(o instanceof DERVisibleString))
         {
             return false;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
new file mode 100644
index 0000000..2a7f2e7
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.asn1;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+class DefiniteLengthInputStream
+        extends LimitedInputStream
+{
+    private static final byte[] EMPTY_BYTES = new byte[0];
+
+    private final int _originalLength;
+    private int _remaining;
+
+    DefiniteLengthInputStream(
+        InputStream in,
+        int         length)
+    {
+        super(in);
+
+        if (length < 0)
+        {
+            throw new IllegalArgumentException("negative lengths not allowed");
+        }
+
+        this._originalLength = length;
+        this._remaining = length;
+
+        if (length == 0)
+        {
+            setParentEofDetect(true);
+        }
+    }
+
+    int getRemaining()
+    {
+        return _remaining;
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (_remaining == 0)
+        {
+            return -1;
+        }
+
+        int b = _in.read();
+
+        if (b < 0)
+        {
+            throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+        }
+
+        if (--_remaining == 0)
+        {
+            setParentEofDetect(true);
+        }
+
+        return b;
+    }
+
+    public int read(byte[] buf, int off, int len)
+        throws IOException
+    {
+        if (_remaining == 0)
+        {
+            return -1;
+        }
+
+        int toRead = Math.min(len, _remaining);
+        int numRead = _in.read(buf, off, toRead);
+
+        if (numRead < 0)
+        {
+            throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+        }
+
+        if ((_remaining -= numRead) == 0)
+        {
+            setParentEofDetect(true);
+        }
+
+        return numRead;
+    }
+
+    byte[] toByteArray()
+        throws IOException
+    {
+        if (_remaining == 0)
+        {
+            return EMPTY_BYTES;
+        }
+
+        byte[] bytes = new byte[_remaining];
+        if ((_remaining -= Streams.readFully(_in, bytes)) != 0)
+        {
+            throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
+        }
+        setParentEofDetect(true);
+        return bytes;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java b/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
new file mode 100644
index 0000000..d9eac06
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.asn1;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+class IndefiniteLengthInputStream
+    extends LimitedInputStream
+{
+    private int _b1;
+    private int _b2;
+    private boolean _eofReached = false;
+    private boolean _eofOn00 = true;
+
+    IndefiniteLengthInputStream(
+        InputStream in)
+        throws IOException
+    {
+        super(in);
+
+        _b1 = in.read();
+        _b2 = in.read();
+
+        if (_b2 < 0)
+        {
+            // Corrupted stream
+            throw new EOFException();
+        }
+
+        checkForEof();
+    }
+
+    void setEofOn00(
+        boolean eofOn00)
+    {
+        _eofOn00 = eofOn00;
+        checkForEof();
+    }
+
+    private boolean checkForEof()
+    {
+        if (!_eofReached && _eofOn00 && (_b1 == 0x00 && _b2 == 0x00))
+        {
+            _eofReached = true;
+            setParentEofDetect(true);
+        }
+        return _eofReached;
+    }
+
+    public int read(byte[] b, int off, int len)
+        throws IOException
+    {
+        // Only use this optimisation if we aren't checking for 00
+        if (_eofOn00 || len < 3)
+        {
+            return super.read(b, off, len);
+        }
+
+        if (_eofReached)
+        {
+            return -1;
+        }
+
+        int numRead = _in.read(b, off + 2, len - 2);
+
+        if (numRead < 0)
+        {
+            // Corrupted stream
+            throw new EOFException();
+        }
+
+        b[off] = (byte)_b1;
+        b[off + 1] = (byte)_b2;
+
+        _b1 = _in.read();
+        _b2 = _in.read();
+
+        if (_b2 < 0)
+        {
+            // Corrupted stream
+            throw new EOFException();
+        }
+
+        return numRead + 2;
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (checkForEof())
+        {
+            return -1;
+        }
+
+        int b = _in.read();
+
+        if (b < 0)
+        {
+            // Corrupted stream
+            throw new EOFException();
+        }
+
+        int v = _b1;
+
+        _b1 = _b2;
+        _b2 = b;
+
+        return v;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java b/src/main/java/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java
new file mode 100644
index 0000000..c5dfbc1
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.asn1;
+
+import java.util.Enumeration;
+import java.io.IOException;
+
+class LazyDERConstructionEnumeration
+    implements Enumeration
+{
+    private ASN1InputStream aIn;
+    private Object          nextObj;
+
+    public LazyDERConstructionEnumeration(byte[] encoded)
+    {
+        aIn = new ASN1InputStream(encoded, true);
+        nextObj = readObject();
+    }
+
+    public boolean hasMoreElements()
+    {
+        return nextObj != null;
+    }
+
+    public Object nextElement()
+    {
+        Object o = nextObj;
+
+        nextObj = readObject();
+
+        return o;
+    }
+
+    private Object readObject()
+    {
+        try
+        {
+            return aIn.readObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("malformed DER construction: " + e, e);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/LazyDERSequence.java b/src/main/java/org/bouncycastle/asn1/LazyDERSequence.java
new file mode 100644
index 0000000..da9e1b8
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/LazyDERSequence.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class LazyDERSequence
+    extends DERSequence
+{
+    private byte[] encoded;
+    private boolean parsed = false;
+    private int size = -1;
+
+    LazyDERSequence(
+        byte[] encoded)
+        throws IOException
+    {
+        this.encoded = encoded;
+    }
+
+    private void parse()
+    {
+        Enumeration en = new LazyDERConstructionEnumeration(encoded);
+
+        while (en.hasMoreElements())
+        {
+            addObject((DEREncodable)en.nextElement());
+        }
+
+        parsed = true;
+    }
+
+    public DEREncodable getObjectAt(int index)
+    {
+        if (!parsed)
+        {
+            parse();
+        }
+
+        return super.getObjectAt(index);
+    }
+
+    public Enumeration getObjects()
+    {
+        if (parsed)
+        {
+            return super.getObjects();
+        }
+
+        return new LazyDERConstructionEnumeration(encoded);
+    }
+
+    public int size()
+    {
+        if (size < 0)
+        {
+            Enumeration en = new LazyDERConstructionEnumeration(encoded);
+
+            size = 0;
+            while (en.hasMoreElements())
+            {
+                en.nextElement();
+                size++;
+            }
+        }
+
+        return size;
+    }
+    
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        out.writeEncoded(SEQUENCE | CONSTRUCTED, encoded);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java b/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
new file mode 100644
index 0000000..5a93335
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.asn1;
+
+import java.io.InputStream;
+
+abstract class LimitedInputStream
+        extends InputStream
+{
+    protected final InputStream _in;
+
+    LimitedInputStream(
+        InputStream in)
+    {
+        this._in = in;
+    }
+
+    protected void setParentEofDetect(boolean on)
+    {
+        if (_in instanceof IndefiniteLengthInputStream)
+        {
+            ((IndefiniteLengthInputStream)_in).setEofOn00(on);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/OrderedTable.java b/src/main/java/org/bouncycastle/asn1/OrderedTable.java
index 511aba2..8db8ab8 100644
--- a/src/main/java/org/bouncycastle/asn1/OrderedTable.java
+++ b/src/main/java/org/bouncycastle/asn1/OrderedTable.java
@@ -54,7 +54,9 @@
     // Note: Default public constructor.
 
     /**
-     * Adds an element.
+     * Adds an element assuming no duplicate key.
+     * 
+     * @see #put
      * 
      * @param key non-null; the key
      * @param value non-null; the value
@@ -145,6 +147,38 @@
     }
     
     /**
+     * Replace a key if present, otherwise add
+     * 
+     * @see #add
+     * 
+     * @param key non-null; the key
+     * @param value non-null; the value
+     */
+    public void put(DERObjectIdentifier key, Object value) {
+        if (key == null) {
+            throw new NullPointerException("key == null");
+        }
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        int keyHash = key.hashCode();
+        int sz = size;
+
+        for (int i = 0; i < size; i++) {
+            DERObjectIdentifier probe = getKey(i);
+            if ((probe.hashCode() == keyHash) &&
+                    probe.equals(key)) {
+                setValue(i, value);
+                return;
+            }
+        }
+
+        add(key, value);
+    }
+    
+    /**
      * Gets the nth key.
      * 
      * @param n index
@@ -185,6 +219,29 @@
     }
 
     /**
+     * Sets the nth value.
+     * 
+     * @param n index
+     * @param value non-null object
+     */
+    public void setValue(int n, Object value) {
+        if ((n < 0) || (n >= size)) {
+            throw new IndexOutOfBoundsException(Integer.toString(n));
+        }
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        switch (n) {
+            case 0: value0 = value; return;
+            case 1: value1 = value; return;
+            case 2: value2 = value; return;
+            case 3: value3 = value; return;
+            default: rest[((n - 4) * 2) + 1] = value; return;
+        }
+    }
+
+    /**
      * Gets an enumeration of the keys, in order.
      * 
      * @return non-null; an enumeration of the keys
diff --git a/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
new file mode 100644
index 0000000..38a91fe
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.asn1.bc;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+
+public interface BCObjectIdentifiers
+{
+    /**
+     *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+     *
+     *  1.3.6.1.4.1.22554
+     */
+    public static final DERObjectIdentifier bc = new DERObjectIdentifier("1.3.6.1.4.1.22554");
+
+    /**
+     * pbe(1) algorithms
+     */
+    public static final DERObjectIdentifier bc_pbe = new DERObjectIdentifier(bc.getId() + ".1");
+
+    /**
+     * SHA-1(1)
+     */
+    public static final DERObjectIdentifier bc_pbe_sha1 = new DERObjectIdentifier(bc_pbe.getId() + ".1");
+
+    /**
+     * SHA-2(2) . (SHA-256(1)|SHA-384(2)|SHA-512(3)|SHA-224(4))
+     */
+    public static final DERObjectIdentifier bc_pbe_sha256 = new DERObjectIdentifier(bc_pbe.getId() + ".2.1");
+    public static final DERObjectIdentifier bc_pbe_sha384 = new DERObjectIdentifier(bc_pbe.getId() + ".2.2");
+    public static final DERObjectIdentifier bc_pbe_sha512 = new DERObjectIdentifier(bc_pbe.getId() + ".2.3");
+    public static final DERObjectIdentifier bc_pbe_sha224 = new DERObjectIdentifier(bc_pbe.getId() + ".2.4");
+
+    /**
+     * PKCS-5(1)|PKCS-12(2)
+     */
+    public static final DERObjectIdentifier bc_pbe_sha1_pkcs5 = new DERObjectIdentifier(bc_pbe_sha1.getId() + ".1");
+    public static final DERObjectIdentifier bc_pbe_sha1_pkcs12 = new DERObjectIdentifier(bc_pbe_sha1.getId() + ".2");
+
+    public static final DERObjectIdentifier bc_pbe_sha256_pkcs5 = new DERObjectIdentifier(bc_pbe_sha256.getId() + ".1");
+    public static final DERObjectIdentifier bc_pbe_sha256_pkcs12 = new DERObjectIdentifier(bc_pbe_sha256.getId() + ".2");
+
+    /**
+     * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
+     */
+    public static final DERObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = new DERObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.2");
+    public static final DERObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = new DERObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.22");
+    public static final DERObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = new DERObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.42");
+
+    public static final DERObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = new DERObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.2");
+    public static final DERObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = new DERObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.22");
+    public static final DERObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = new DERObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.42");
+}
diff --git a/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java b/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java
deleted file mode 100644
index 5d97e0a..0000000
--- a/src/main/java/org/bouncycastle/asn1/cmp/PKIFailureInfo.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.bouncycastle.asn1.cmp;
-
-import org.bouncycastle.asn1.DERBitString;
-
-/**
- * <pre>
- * PKIFailureInfo ::= BIT STRING {
- * badAlg               (0),
- *   -- unrecognized or unsupported Algorithm Identifier
- * badMessageCheck      (1), -- integrity check failed (e.g., signature did not verify)
- * badRequest           (2),
- *   -- transaction not permitted or supported
- * badTime              (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
- * badCertId            (4), -- no certificate could be found matching the provided criteria
- * badDataFormat        (5),
- *   -- the data submitted has the wrong format
- * wrongAuthority       (6), -- the authority indicated in the request is different from the one creating the response token
- * incorrectData        (7), -- the requester's data is incorrect (for notary services)
- * missingTimeStamp     (8), -- when the timestamp is missing but should be there (by policy)
- * badPOP               (9)  -- the proof-of-possession failed
- * timeNotAvailable    (14),
- *   -- the TSA's time source is not available
- * unacceptedPolicy    (15),
- *   -- the requested TSA policy is not supported by the TSA
- * unacceptedExtension (16),
- *   -- the requested extension is not supported by the TSA
- *  addInfoNotAvailable (17)
- *    -- the additional information requested could not be understood
- *    -- or is not available
- *  systemFailure       (25)
- *    -- the request cannot be handled due to system failure 
- * </pre>
- */
-public class PKIFailureInfo
-    extends DERBitString
-{
-
-
-    public static final int badAlg               = (1 << 7); // unrecognized or unsupported Algorithm Identifier
-    public static final int badMessageCheck      = (1 << 6); // integrity check failed (e.g., signature did not verify)
-    public static final int badRequest           = (1 << 5);
-    public static final int badTime              = (1 << 4); // -- messageTime was not sufficiently close to the system time, as defined by local policy
-    public static final int badCertId            = (1 << 3); // no certificate could be found matching the provided criteria
-    public static final int badDataFormat        = (1 << 2);
-    public static final int wrongAuthority       = (1 << 1); // the authority indicated in the request is different from the one creating the response token
-    public static final int incorrectData        = 1;        // the requester's data is incorrect (for notary services)
-    public static final int missingTimeStamp     = (1 << 15); // when the timestamp is missing but should be there (by policy)
-    public static final int badPOP               = (1 << 14); // the proof-of-possession failed
-    public static final int timeNotAvailable     = (1 << 9); // the TSA's time source is not available
-    public static final int unacceptedPolicy     = (1 << 8); // the requested TSA policy is not supported by the TSA
-    public static final int unacceptedExtension  = (1 << 23); //the requested extension is not supported by the TSA
-    public static final int addInfoNotAvailable  = (1 << 22); //the additional information requested could not be understood or is not available
-    public static final int systemFailure        = (1 << 30); //the request cannot be handled due to system failure
-    
-    /** @deprecated use lower case version */
-    public static final int BAD_ALG                   = badAlg; // unrecognized or unsupported Algorithm Identifier
-    /** @deprecated use lower case version */
-    public static final int BAD_MESSAGE_CHECK         = badMessageCheck;
-    /** @deprecated use lower case version */
-    public static final int BAD_REQUEST               = badRequest; // transaction not permitted or supported
-    /** @deprecated use lower case version */
-    public static final int BAD_TIME                  = badTime;
-    /** @deprecated use lower case version */
-    public static final int BAD_CERT_ID               = badCertId;
-    /** @deprecated use lower case version */
-    public static final int BAD_DATA_FORMAT           = badDataFormat; // the data submitted has the wrong format
-    /** @deprecated use lower case version */
-    public static final int WRONG_AUTHORITY           = wrongAuthority;
-    /** @deprecated use lower case version */
-    public static final int INCORRECT_DATA            = incorrectData;
-    /** @deprecated use lower case version */
-    public static final int MISSING_TIME_STAMP        = missingTimeStamp;
-    /** @deprecated use lower case version */
-    public static final int BAD_POP                   = badPOP;
-    /** @deprecated use lower case version */
-    public static final int TIME_NOT_AVAILABLE        = timeNotAvailable;
-    /** @deprecated use lower case version */
-    public static final int UNACCEPTED_POLICY         = unacceptedPolicy;
-    /** @deprecated use lower case version */
-    public static final int UNACCEPTED_EXTENSION      = unacceptedExtension;
-    /** @deprecated use lower case version */
-    public static final int ADD_INFO_NOT_AVAILABLE    = addInfoNotAvailable; 
-    /** @deprecated use lower case version */
-    public static final int SYSTEM_FAILURE            = systemFailure; 
-    /**
-     * Basic constructor.
-     */
-    public PKIFailureInfo(
-        int info)
-    {
-        super(getBytes(info), getPadBits(info));
-    }
-
-    public PKIFailureInfo(
-        DERBitString info)
-    {
-        super(info.getBytes(), info.getPadBits());
-    }
-    
-    public String toString()
-    {
-        return "PKIFailureInfo: 0x" + Integer.toHexString(this.intValue());
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java b/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java
deleted file mode 100644
index f821071..0000000
--- a/src/main/java/org/bouncycastle/asn1/cmp/PKIFreeText.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.bouncycastle.asn1.cmp;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERUTF8String;
-
-public class PKIFreeText
-    extends ASN1Encodable
-{
-    ASN1Sequence strings;
-
-    public static PKIFreeText getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static PKIFreeText getInstance(
-        Object obj)
-    {
-        if (obj instanceof PKIFreeText)
-        {
-            return (PKIFreeText)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new PKIFreeText((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("Unknown object in factory");
-    }
-
-    public PKIFreeText(
-        ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-        while (e.hasMoreElements())
-        {
-            if (!(e.nextElement() instanceof DERUTF8String))
-            {
-                throw new IllegalArgumentException("attempt to insert non UTF8 STRING into PKIFreeText");
-            }
-        }
-        
-        strings = seq;
-    }
-
-    public PKIFreeText(
-        DERUTF8String p)
-    {
-        strings = new DERSequence(p);
-    }
-
-    /**
-     * Return the number of string elements present.
-     * 
-     * @return number of elements present.
-     */
-    public int size()
-    {
-        return strings.size();
-    }
-    
-    /**
-     * Return the UTF8STRING at index i.
-     * 
-     * @param i index of the string of interest
-     * @return the string at index i.
-     */
-    public DERUTF8String getStringAt(
-        int i)
-    {
-        return (DERUTF8String)strings.getObjectAt(i);
-    }
-    
-    /**
-     * <pre>
-     * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return strings;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java b/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java
deleted file mode 100644
index 58b2b69..0000000
--- a/src/main/java/org/bouncycastle/asn1/cmp/PKIStatus.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.bouncycastle.asn1.cmp;
-
-public interface PKIStatus
-{
-
-  public static final int GRANTED                 = 0;
-  public static final int GRANTED_WITH_MODS       = 1;
-  public static final int REJECTION               = 2;
-  public static final int WAITING                 = 3;
-  public static final int REVOCATION_WARNING      = 4;
-  public static final int REVOCATION_NOTIFICATION = 5;
-
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java b/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java
deleted file mode 100644
index b4f37cc..0000000
--- a/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.bouncycastle.asn1.cmp;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-public class PKIStatusInfo
-    extends ASN1Encodable
-{
-    DERInteger      status;
-    PKIFreeText     statusString;
-    DERBitString    failInfo;
-
-    public static PKIStatusInfo getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static PKIStatusInfo getInstance(
-        Object obj)
-    {
-        if (obj instanceof PKIStatusInfo)
-        {
-            return (PKIStatusInfo)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new PKIStatusInfo((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass());
-    }
-
-    public PKIStatusInfo(
-        ASN1Sequence seq)
-    {
-        this.status = DERInteger.getInstance(seq.getObjectAt(0));
-
-        this.statusString = null;
-        this.failInfo = null;
-
-        if (seq.size() > 2)
-        {
-            this.statusString = PKIFreeText.getInstance(seq.getObjectAt(1));
-            this.failInfo = DERBitString.getInstance(seq.getObjectAt(2));
-        }
-        else if (seq.size() > 1)
-        {
-            Object obj = seq.getObjectAt(1); 
-            if (obj instanceof DERBitString)
-            {
-                this.failInfo = DERBitString.getInstance(obj);
-            }
-            else
-            {
-                this.statusString = PKIFreeText.getInstance(obj);
-            }
-        }
-    }
-
-    /**
-     * @param status
-     */
-    public PKIStatusInfo(int status)
-    {
-        this.status = new DERInteger(status);
-    }
-
-    /**
-     * @param status
-     * @param statusString
-     */
-    public PKIStatusInfo(
-        int         status,
-        PKIFreeText statusString)
-    {
-        this.status = new DERInteger(status);
-        this.statusString = statusString;
-    }
-
-    public PKIStatusInfo(
-            int            status,
-            PKIFreeText    statusString,
-            PKIFailureInfo failInfo)
-    {
-        this.status = new DERInteger(status);
-        this.statusString = statusString;
-        this.failInfo = failInfo;
-    }
-    
-    public BigInteger getStatus()
-    {
-        return status.getValue();
-    }
-
-    public PKIFreeText getStatusString()
-    {
-        return statusString;
-    }
-
-    public DERBitString getFailInfo()
-    {
-        return failInfo;
-    }
-
-    /**
-     * <pre>
-     * PKIStatusInfo ::= SEQUENCE {
-     *     status        PKIStatus,                (INTEGER)
-     *     statusString  PKIFreeText     OPTIONAL,
-     *     failInfo      PKIFailureInfo  OPTIONAL  (BIT STRING)
-     * }
-     *
-     * PKIStatus:
-     *   granted                (0), -- you got exactly what you asked for
-     *   grantedWithMods        (1), -- you got something like what you asked for
-     *   rejection              (2), -- you don't get it, more information elsewhere in the message
-     *   waiting                (3), -- the request body part has not yet been processed, expect to hear more later
-     *   revocationWarning      (4), -- this message contains a warning that a revocation is imminent
-     *   revocationNotification (5), -- notification that a revocation has occurred
-     *   keyUpdateWarning       (6)  -- update already done for the oldCertId specified in CertReqMsg
-     *
-     * PKIFailureInfo:
-     *   badAlg           (0), -- unrecognized or unsupported Algorithm Identifier
-     *   badMessageCheck  (1), -- integrity check failed (e.g., signature did not verify)
-     *   badRequest       (2), -- transaction not permitted or supported
-     *   badTime          (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
-     *   badCertId        (4), -- no certificate could be found matching the provided criteria
-     *   badDataFormat    (5), -- the data submitted has the wrong format
-     *   wrongAuthority   (6), -- the authority indicated in the request is different from the one creating the response token
-     *   incorrectData    (7), -- the requester's data is incorrect (for notary services)
-     *   missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy)
-     *   badPOP           (9)  -- the proof-of-possession failed
-     *
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(status);
-
-        if (statusString != null)
-        {
-            v.add(statusString);
-        }
-
-        if (failInfo!= null)
-        {
-            v.add(failInfo);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/Attribute.java b/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
deleted file mode 100644
index dbec62e..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class Attribute
-    extends ASN1Encodable
-{
-    private DERObjectIdentifier attrType;
-    private ASN1Set             attrValues;
-
-    /**
-     * return an Attribute object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static Attribute getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof Attribute)
-        {
-            return (Attribute)o;
-        }
-        
-        if (o instanceof ASN1Sequence)
-        {
-            return new Attribute((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-    
-    public Attribute(
-        ASN1Sequence seq)
-    {
-        attrType = (DERObjectIdentifier)seq.getObjectAt(0);
-        attrValues = (ASN1Set)seq.getObjectAt(1);
-    }
-
-    public Attribute(
-        DERObjectIdentifier attrType,
-        ASN1Set             attrValues)
-    {
-        this.attrType = attrType;
-        this.attrValues = attrValues;
-    }
-
-    public DERObjectIdentifier getAttrType()
-    {
-        return attrType;
-    }
-    
-    public ASN1Set getAttrValues()
-    {
-        return attrValues;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Attribute ::= SEQUENCE {
-     *     attrType OBJECT IDENTIFIER,
-     *     attrValues SET OF AttributeValue
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(attrType);
-        v.add(attrValues);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java b/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
deleted file mode 100644
index 9b4d79f..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DEREncodableVector;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public class AttributeTable
-{
-    private Hashtable attributes = new Hashtable();
-
-    public AttributeTable(
-        Hashtable  attrs)
-    {
-        attributes = copyTable(attrs);
-    }
-
-    public AttributeTable(
-        DEREncodableVector v)
-    {
-        for (int i = 0; i != v.size(); i++)
-        {
-            Attribute   a = Attribute.getInstance(v.get(i));
-
-            addAttribute(a.getAttrType(), a);
-        }
-    }
-
-    public AttributeTable(
-        ASN1Set    s)
-    {
-        for (int i = 0; i != s.size(); i++)
-        {
-            Attribute   a = Attribute.getInstance(s.getObjectAt(i));
-
-            addAttribute(a.getAttrType(), a);
-        }
-    }
-
-    private void addAttribute(
-        DERObjectIdentifier oid,
-        Attribute           a)
-    {
-        Object value = attributes.get(oid);
-        
-        if (value == null)
-        {
-            attributes.put(oid, a);
-        }
-        else
-        {
-            Vector v;
-            
-            if (value instanceof Attribute)
-            {
-                v = new Vector();
-                
-                v.addElement(value);
-                v.addElement(a);
-            }
-            else
-            {
-                v = (Vector)value;
-            
-                v.addElement(a);
-            }
-            
-            attributes.put(oid, v);
-        }
-    }
-    
-    /**
-     * Return the first attribute matching the OBJECT IDENTIFIER oid.
-     * 
-     * @param oid type of attribute required.
-     * @return first attribute found of type oid.
-     */
-    public Attribute get(
-        DERObjectIdentifier oid)
-    {
-        Object value = attributes.get(oid);
-        
-        if (value instanceof Vector)
-        {
-            return (Attribute)((Vector)value).elementAt(0);
-        }
-        
-        return (Attribute)value;
-    }
-
-    /**
-     * Return all the attributes matching the OBJECT IDENTIFIER oid. The vector will be 
-     * empty if there are no attributes of the required type present.
-     * 
-     * @param oid type of attribute required.
-     * @return a vector of all the attributes found of type oid.
-     */
-    public ASN1EncodableVector getAll(
-        DERObjectIdentifier oid)
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        Object value = attributes.get(oid);
-        
-        if (value instanceof Vector)
-        {
-            Enumeration e = ((Vector)value).elements();
-            
-            while (e.hasMoreElements())
-            {
-                v.add((Attribute)e.nextElement());
-            }
-        }
-        else if (value != null)
-        {
-            v.add((Attribute)value);
-        }
-        
-        return v;
-    }
-    
-    public Hashtable toHashtable()
-    {
-        return copyTable(attributes);
-    }
-    
-    public ASN1EncodableVector toASN1EncodableVector()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-        Enumeration          e = attributes.elements();
-        
-        while (e.hasMoreElements())
-        {
-            Object value = e.nextElement();
-            
-            if (value instanceof Vector)
-            {
-                Enumeration en = ((Vector)value).elements();
-                
-                while (en.hasMoreElements())
-                {
-                    v.add(Attribute.getInstance(en.nextElement()));
-                }
-            }
-            else
-            {
-                v.add(Attribute.getInstance(value));
-            }
-        }
-        
-        return v;
-    }
-    
-    private Hashtable copyTable(
-        Hashtable in)
-    {
-        Hashtable   out = new Hashtable();
-        Enumeration e = in.keys();
-        
-        while (e.hasMoreElements())
-        {
-            Object key = e.nextElement();
-            
-            out.put(key, in.get(key));
-        }
-        
-        return out;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
deleted file mode 100644
index bdcca6a..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-
-public interface CMSAttributes
-{
-    public static final DERObjectIdentifier  contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
-    public static final DERObjectIdentifier  messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
-    public static final DERObjectIdentifier  signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
-    public static final DERObjectIdentifier  counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
index 2e6b312..88e7c18 100644
--- a/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
@@ -11,5 +11,7 @@
     static final DERObjectIdentifier    signedAndEnvelopedData = PKCSObjectIdentifiers.signedAndEnvelopedData;
     static final DERObjectIdentifier    digestedData = PKCSObjectIdentifiers.digestedData;
     static final DERObjectIdentifier    encryptedData = PKCSObjectIdentifiers.encryptedData;
+    static final DERObjectIdentifier    authenticatedData = PKCSObjectIdentifiers.id_ct_authData;
     static final DERObjectIdentifier    compressedData = PKCSObjectIdentifiers.id_ct_compressedData;
+    static final DERObjectIdentifier    authEnvelopedData = PKCSObjectIdentifiers.id_ct_authEnvelopedData;
 }
diff --git a/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java b/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
deleted file mode 100644
index 2331059..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.BERSequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-/** 
- * RFC 3274 - CMS Compressed Data.
- * <pre>
- * CompressedData ::= SEQUENCE {
- *  version CMSVersion,
- *  compressionAlgorithm CompressionAlgorithmIdentifier,
- *  encapContentInfo EncapsulatedContentInfo
- * }
- * </pre>
- */
-public class CompressedData
-    extends ASN1Encodable
-{
-    private DERInteger           version;
-    private AlgorithmIdentifier  compressionAlgorithm;
-    private ContentInfo          encapContentInfo;
-
-    public CompressedData(
-        AlgorithmIdentifier compressionAlgorithm,
-        ContentInfo         encapContentInfo)
-    {
-        this.version = new DERInteger(0);
-        this.compressionAlgorithm = compressionAlgorithm;
-        this.encapContentInfo = encapContentInfo;
-    }
-    
-    public CompressedData(
-        ASN1Sequence seq)
-    {
-        this.version = (DERInteger)seq.getObjectAt(0);
-        this.compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-        this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2));
-
-    }
-
-    /**
-     * return a CompressedData object from a tagged object.
-     *
-     * @param _ato the tagged object holding the object we want.
-     * @param _explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static CompressedData getInstance(
-        ASN1TaggedObject _ato,
-        boolean _explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
-    }
-    
-    /**
-     * return a CompressedData object from the given object.
-     *
-     * @param _obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static CompressedData getInstance(
-        Object _obj)
-    {
-        if (_obj == null || _obj instanceof CompressedData)
-        {
-            return (CompressedData)_obj;
-        }
-        
-        if (_obj instanceof ASN1Sequence)
-        {
-            return new CompressedData((ASN1Sequence)_obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid CompressedData: " + _obj.getClass().getName());
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public AlgorithmIdentifier getCompressionAlgorithmIdentifier()
-    {
-        return compressionAlgorithm;
-    }
-
-    public ContentInfo getEncapContentInfo()
-    {
-        return encapContentInfo;
-    }
-
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(compressionAlgorithm);
-        v.add(encapContentInfo);
-
-        return new BERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
index 76801a3..8ab346d 100644
--- a/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -22,7 +22,7 @@
     public static ContentInfo getInstance(
         Object  obj)
     {
-        if (obj instanceof ContentInfo)
+        if (obj == null || obj instanceof ContentInfo)
         {
             return (ContentInfo)obj;
         }
diff --git a/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java b/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
deleted file mode 100644
index 22ac839..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.BERSequence;
-import org.bouncycastle.asn1.BERTaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class EncryptedContentInfo
-    extends ASN1Encodable
-{
-    private DERObjectIdentifier contentType;
-    private AlgorithmIdentifier contentEncryptionAlgorithm;
-    private ASN1OctetString     encryptedContent;
-    
-    public EncryptedContentInfo(
-        DERObjectIdentifier contentType, 
-        AlgorithmIdentifier contentEncryptionAlgorithm,
-        ASN1OctetString     encryptedContent)
-    {
-        this.contentType = contentType;
-        this.contentEncryptionAlgorithm = contentEncryptionAlgorithm;
-        this.encryptedContent = encryptedContent;
-    }
-    
-    public EncryptedContentInfo(
-        ASN1Sequence seq)
-    {
-        contentType = (DERObjectIdentifier)seq.getObjectAt(0);
-        contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
-                                                        seq.getObjectAt(1));
-        if (seq.size() > 2)
-        {
-            encryptedContent = ASN1OctetString.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(2), false);
-        }
-    }
-
-    /**
-     * return an EncryptedContentInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static EncryptedContentInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof EncryptedContentInfo)
-        {
-            return (EncryptedContentInfo)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new EncryptedContentInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid EncryptedContentInfo: "
-                                                + obj.getClass().getName());
-    }
-
-    public DERObjectIdentifier getContentType()
-    {
-        return contentType;
-    }
-
-    public AlgorithmIdentifier getContentEncryptionAlgorithm()
-    {
-        return contentEncryptionAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedContent()
-    {
-        return encryptedContent;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * EncryptedContentInfo ::= SEQUENCE {
-     *     contentType ContentType,
-     *     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
-     *     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-        
-        v.add(contentType);
-        v.add(contentEncryptionAlgorithm);
-
-        if (encryptedContent != null)
-        {
-            v.add(new BERTaggedObject(false, 0, encryptedContent));
-        }
-        
-        return new BERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java b/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
deleted file mode 100644
index f4ac42c..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.BERSequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class EnvelopedData
-    extends ASN1Encodable
-{
-    private DERInteger              version;
-    private OriginatorInfo          originatorInfo;
-    private ASN1Set                 recipientInfos;
-    private EncryptedContentInfo    encryptedContentInfo;
-    private ASN1Set                 unprotectedAttrs;
-
-    public EnvelopedData(
-        OriginatorInfo          originatorInfo,
-        ASN1Set                 recipientInfos,
-        EncryptedContentInfo    encryptedContentInfo,
-        ASN1Set                 unprotectedAttrs)
-    {
-        if (originatorInfo != null || unprotectedAttrs != null)
-        {
-            version = new DERInteger(2);
-        }
-        else
-        {
-            version = new DERInteger(0);
-
-            Enumeration e = recipientInfos.getObjects();
-
-            while (e.hasMoreElements())
-            {
-                RecipientInfo   ri = RecipientInfo.getInstance(e.nextElement());
-
-                if (!ri.getVersion().equals(version))
-                {
-                    version = new DERInteger(2);
-                    break;
-                }
-            }
-        }
-
-        this.originatorInfo = originatorInfo;
-        this.recipientInfos = recipientInfos;
-        this.encryptedContentInfo = encryptedContentInfo;
-        this.unprotectedAttrs = unprotectedAttrs;
-    }
-                         
-    public EnvelopedData(
-        ASN1Sequence seq)
-    {
-        int     index = 0;
-        
-        version = (DERInteger)seq.getObjectAt(index++);
-        
-        Object  tmp = seq.getObjectAt(index++);
-
-        if (tmp instanceof ASN1TaggedObject)
-        {
-            originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
-            tmp = seq.getObjectAt(index++);
-        }
-
-        recipientInfos = ASN1Set.getInstance(tmp);
-        
-        encryptedContentInfo = EncryptedContentInfo.getInstance(seq.getObjectAt(index++));
-        
-        if(seq.size() > index)
-        {
-            unprotectedAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false);
-        }
-    }
-    
-    /**
-     * return an EnvelopedData object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static EnvelopedData getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return an EnvelopedData object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static EnvelopedData getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof EnvelopedData)
-        {
-            return (EnvelopedData)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new EnvelopedData((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid EnvelopedData: " + obj.getClass().getName());
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-    
-    public OriginatorInfo getOriginatorInfo()
-    {
-        return originatorInfo;
-    }
-
-    public ASN1Set getRecipientInfos()
-    {
-        return recipientInfos;
-    }
-
-    public EncryptedContentInfo getEncryptedContentInfo()
-    {
-        return encryptedContentInfo;
-    }
-
-    public ASN1Set getUnprotectedAttrs()
-    {
-        return unprotectedAttrs;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * EnvelopedData ::= SEQUENCE {
-     *     version CMSVersion,
-     *     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
-     *     recipientInfos RecipientInfos,
-     *     encryptedContentInfo EncryptedContentInfo,
-     *     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-        
-        v.add(version);
-
-        if (originatorInfo != null)
-        {
-            v.add(new DERTaggedObject(false, 0, originatorInfo));
-        }
-
-        v.add(recipientInfos);
-        v.add(encryptedContentInfo);
-
-        if (unprotectedAttrs != null)
-        {
-            v.add(new DERTaggedObject(false, 1, unprotectedAttrs));
-        }
-        
-        return new BERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java b/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
deleted file mode 100644
index f02b1aa..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.X509Name;
-
-public class IssuerAndSerialNumber
-    extends ASN1Encodable
-{
-    X509Name    name;
-    DERInteger  serialNumber;
-
-    public static IssuerAndSerialNumber getInstance(
-        Object  obj)
-    {
-        if (obj instanceof IssuerAndSerialNumber)
-        {
-            return (IssuerAndSerialNumber)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new IssuerAndSerialNumber((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException(
-            "Illegal object in IssuerAndSerialNumber: " + obj.getClass().getName());
-    }
-
-    public IssuerAndSerialNumber(
-        ASN1Sequence    seq)
-    {
-        this.name = X509Name.getInstance(seq.getObjectAt(0));
-        this.serialNumber = (DERInteger)seq.getObjectAt(1);
-    }
-
-    public IssuerAndSerialNumber(
-        X509Name    name,
-        BigInteger  serialNumber)
-    {
-        this.name = name;
-        this.serialNumber = new DERInteger(serialNumber);
-    }
-
-    public IssuerAndSerialNumber(
-        X509Name    name,
-        DERInteger  serialNumber)
-    {
-        this.name = name;
-        this.serialNumber = serialNumber;
-    }
-
-    public X509Name getName()
-    {
-        return name;
-    }
-
-    public DERInteger getSerialNumber()
-    {
-        return serialNumber;
-    }
-
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(name);
-        v.add(serialNumber);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java b/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
deleted file mode 100644
index 708487e..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-
-public class KEKIdentifier
-    extends ASN1Encodable
-{
-    private ASN1OctetString    keyIdentifier;
-    private DERGeneralizedTime date;
-    private OtherKeyAttribute  other;
-    
-    public KEKIdentifier(
-        byte[]              keyIdentifier,
-        DERGeneralizedTime  date,
-        OtherKeyAttribute   other)
-    {
-        this.keyIdentifier = new DEROctetString(keyIdentifier);
-        this.date = date;
-        this.other = other;
-    }
-    
-    public KEKIdentifier(
-        ASN1Sequence seq)
-    {
-        keyIdentifier = (ASN1OctetString)seq.getObjectAt(0);
-        
-        switch (seq.size())
-        {
-        case 1:
-            break;
-        case 2:
-            if (seq.getObjectAt(1) instanceof DERGeneralizedTime)
-            {
-                date = (DERGeneralizedTime)seq.getObjectAt(1); 
-            }
-            else
-            {
-                other = OtherKeyAttribute.getInstance(seq.getObjectAt(1));
-            }
-            break;
-        case 3:
-            date  = (DERGeneralizedTime)seq.getObjectAt(1);
-            other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
-            break;
-        default:
-                throw new IllegalArgumentException("Invalid KEKIdentifier");
-        }
-    }
-
-    /**
-     * return a KEKIdentifier object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static KEKIdentifier getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return a KEKIdentifier object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static KEKIdentifier getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof KEKIdentifier)
-        {
-            return (KEKIdentifier)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new KEKIdentifier((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid KEKIdentifier: " + obj.getClass().getName());
-    }
-
-    public ASN1OctetString getKeyIdentifier()
-    {
-        return keyIdentifier;
-    }
-
-    public DERGeneralizedTime getDate()
-    {
-        return date;
-    }
-
-    public OtherKeyAttribute getOther()
-    {
-        return other;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KEKIdentifier ::= SEQUENCE {
-     *     keyIdentifier OCTET STRING,
-     *     date GeneralizedTime OPTIONAL,
-     *     other OtherKeyAttribute OPTIONAL 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(keyIdentifier);
-        
-        if (date != null)
-        {
-            v.add(date);
-        }
-
-        if (other != null)
-        {
-            v.add(other);
-        }
-        
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
deleted file mode 100644
index ddbcf13..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class KEKRecipientInfo
-    extends ASN1Encodable
-{
-    private DERInteger          version;
-    private KEKIdentifier       kekid;
-    private AlgorithmIdentifier keyEncryptionAlgorithm;
-    private ASN1OctetString     encryptedKey;
-
-    public KEKRecipientInfo(
-        KEKIdentifier       kekid,
-        AlgorithmIdentifier keyEncryptionAlgorithm,
-        ASN1OctetString     encryptedKey)
-    {
-        this.version = new DERInteger(4);
-        this.kekid = kekid;
-        this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-        this.encryptedKey = encryptedKey;
-    }
-    
-    public KEKRecipientInfo(
-        ASN1Sequence seq)
-    {
-        version = (DERInteger)seq.getObjectAt(0);
-        kekid = KEKIdentifier.getInstance(seq.getObjectAt(1));
-        keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
-        encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
-    }
-
-    /**
-     * return a KEKRecipientInfo object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static KEKRecipientInfo getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return a KEKRecipientInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static KEKRecipientInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof KEKRecipientInfo)
-        {
-            return (KEKRecipientInfo)obj;
-        }
-        
-        if(obj instanceof ASN1Sequence)
-        {
-            return new KEKRecipientInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid KEKRecipientInfo: " + obj.getClass().getName());
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-    
-    public KEKIdentifier getKekid()
-    {
-        return kekid;
-    }
-
-    public AlgorithmIdentifier getKeyEncryptionAlgorithm()
-    {
-        return keyEncryptionAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedKey()
-    {
-        return encryptedKey;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KEKRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 4
-     *     kekid KEKIdentifier,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     encryptedKey EncryptedKey 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(kekid);
-        v.add(keyEncryptionAlgorithm);
-        v.add(encryptedKey);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
deleted file mode 100644
index 5ebf4dc..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class KeyAgreeRecipientInfo
-    extends ASN1Encodable
-{
-    private DERInteger                  version;
-    private OriginatorIdentifierOrKey   originator;
-    private ASN1OctetString             ukm;
-    private AlgorithmIdentifier         keyEncryptionAlgorithm;
-    private ASN1Sequence                recipientEncryptedKeys;
-    
-    public KeyAgreeRecipientInfo(
-        OriginatorIdentifierOrKey   originator,
-        ASN1OctetString             ukm,
-        AlgorithmIdentifier         keyEncryptionAlgorithm,
-        ASN1Sequence                recipientEncryptedKeys)
-    {
-        this.version = new DERInteger(3);
-        this.originator = originator;
-        this.ukm = ukm;
-        this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-        this.recipientEncryptedKeys = recipientEncryptedKeys;
-    }
-    
-    public KeyAgreeRecipientInfo(
-        ASN1Sequence seq)
-    {
-        int index = 0;
-        
-        version = (DERInteger)seq.getObjectAt(index++);
-        originator = OriginatorIdentifierOrKey.getInstance(
-                            (ASN1TaggedObject)seq.getObjectAt(index++), true);
-
-        if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
-        {
-            ukm = ASN1OctetString.getInstance(
-                            (ASN1TaggedObject)seq.getObjectAt(index++), true);
-        }
-
-        keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
-                                                seq.getObjectAt(index++));
-
-        recipientEncryptedKeys = (ASN1Sequence)seq.getObjectAt(index++);
-    }
-    
-    /**
-     * return a KeyAgreeRecipientInfo object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static KeyAgreeRecipientInfo getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return a KeyAgreeRecipientInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static KeyAgreeRecipientInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof KeyAgreeRecipientInfo)
-        {
-            return (KeyAgreeRecipientInfo)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new KeyAgreeRecipientInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException(
-        "Illegal object in KeyAgreeRecipientInfo: " + obj.getClass().getName());
-
-    } 
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public OriginatorIdentifierOrKey getOriginator()
-    {
-        return originator;
-    }
-
-    public ASN1OctetString getUserKeyingMaterial()
-    {
-        return ukm;
-    }
-
-    public AlgorithmIdentifier getKeyEncryptionAlgorithm()
-    {
-        return keyEncryptionAlgorithm;
-    }
-
-    public ASN1Sequence getRecipientEncryptedKeys()
-    {
-        return recipientEncryptedKeys;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KeyAgreeRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 3
-     *     originator [0] EXPLICIT OriginatorIdentifierOrKey,
-     *     ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     recipientEncryptedKeys RecipientEncryptedKeys 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(new DERTaggedObject(true, 0, originator));
-        
-        if (ukm != null)
-        {
-            v.add(new DERTaggedObject(true, 1, ukm));
-        }
-        
-        v.add(keyEncryptionAlgorithm);
-        v.add(recipientEncryptedKeys);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
deleted file mode 100644
index 70553b7..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class KeyTransRecipientInfo
-    extends ASN1Encodable
-{
-    private DERInteger          version;
-    private RecipientIdentifier rid;
-    private AlgorithmIdentifier keyEncryptionAlgorithm;
-    private ASN1OctetString     encryptedKey;
-
-    public KeyTransRecipientInfo(
-        RecipientIdentifier rid,
-        AlgorithmIdentifier keyEncryptionAlgorithm,
-        ASN1OctetString     encryptedKey)
-    {
-        if (rid.getDERObject() instanceof ASN1TaggedObject)
-        {
-            this.version = new DERInteger(2);
-        }
-        else
-        {
-            this.version = new DERInteger(0);
-        }
-
-        this.rid = rid;
-        this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-        this.encryptedKey = encryptedKey;
-    }
-    
-    public KeyTransRecipientInfo(
-        ASN1Sequence seq)
-    {
-        this.version = (DERInteger)seq.getObjectAt(0);
-        this.rid = RecipientIdentifier.getInstance(seq.getObjectAt(1));
-        this.keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
-        this.encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
-    }
-
-    /**
-     * return a KeyTransRecipientInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static KeyTransRecipientInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof KeyTransRecipientInfo)
-        {
-            return (KeyTransRecipientInfo)obj;
-        }
-        
-        if(obj instanceof ASN1Sequence)
-        {
-            return new KeyTransRecipientInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException(
-        "Illegal object in KeyTransRecipientInfo: " + obj.getClass().getName());
-    } 
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public RecipientIdentifier getRecipientIdentifier()
-    {
-        return rid;
-    }
-
-    public AlgorithmIdentifier getKeyEncryptionAlgorithm()
-    {
-        return keyEncryptionAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedKey()
-    {
-        return encryptedKey;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KeyTransRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 0 or 2
-     *     rid RecipientIdentifier,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     encryptedKey EncryptedKey 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(rid);
-        v.add(keyEncryptionAlgorithm);
-        v.add(encryptedKey);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java b/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
deleted file mode 100644
index f9d3c21..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class OriginatorIdentifierOrKey
-    extends ASN1Encodable
-{
-    private DEREncodable id;
-    
-    public OriginatorIdentifierOrKey(
-        IssuerAndSerialNumber id)
-    {
-        this.id = id;
-    }
-    
-    public OriginatorIdentifierOrKey(
-        ASN1OctetString id)
-    {
-        this.id = new DERTaggedObject(false, 0, id);
-    }
-    
-    public OriginatorIdentifierOrKey(
-        OriginatorPublicKey id)
-    {
-        this.id = new DERTaggedObject(false, 1, id);
-    }
-    
-    public OriginatorIdentifierOrKey(
-        DERObject id)
-    {
-        this.id = id;
-    }
-    
-    /**
-     * return an OriginatorIdentifierOrKey object from a tagged object.
-     *
-     * @param o the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static OriginatorIdentifierOrKey getInstance(
-        ASN1TaggedObject    o,
-        boolean             explicit)
-    {
-        if (!explicit)
-        {
-            throw new IllegalArgumentException(
-                    "Can't implicitly tag OriginatorIdentifierOrKey");
-        }
-
-        return getInstance(o.getObject());
-    }
-    
-    /**
-     * return an OriginatorIdentifierOrKey object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static OriginatorIdentifierOrKey getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof OriginatorIdentifierOrKey)
-        {
-            return (OriginatorIdentifierOrKey)o;
-        }
-        
-        if (o instanceof DERObject)
-        {
-            return new OriginatorIdentifierOrKey((DERObject)o);
-        }
-        
-        throw new IllegalArgumentException("Invalid OriginatorIdentifierOrKey: " + o.getClass().getName());
-    } 
-
-    public DEREncodable getId()
-    {
-        return id;
-    }
-    
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorIdentifierOrKey ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier,
-     *     originatorKey [1] OriginatorPublicKey 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return id.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java b/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
deleted file mode 100644
index 50d2edc..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class OriginatorInfo
-    extends ASN1Encodable
-{
-    private ASN1Set certs;
-    private ASN1Set crls;
-    
-    public OriginatorInfo(
-        ASN1Set certs,
-        ASN1Set crls)
-    {
-        this.certs = certs;
-        this.crls = crls;
-    }
-    
-    public OriginatorInfo(
-        ASN1Sequence seq)
-    {
-        switch (seq.size())
-        {
-        case 0:     // empty
-            break;
-        case 1:
-            ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
-            switch (o.getTagNo())
-            {
-            case 0 :
-                certs = ASN1Set.getInstance(o, false);
-                break;
-            case 1 :
-                crls = ASN1Set.getInstance(o, false);
-                break;
-            default:
-                throw new IllegalArgumentException("Bad tag in OriginatorInfo: " + o.getTagNo());
-            }
-            break;
-        case 2:
-            certs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(0), false);
-            crls  = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
-            break;
-        default:
-            throw new IllegalArgumentException("OriginatorInfo too big");
-        }
-    }
-    
-    /**
-     * return an OriginatorInfo object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static OriginatorInfo getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return an OriginatorInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static OriginatorInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof OriginatorInfo)
-        {
-            return (OriginatorInfo)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new OriginatorInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid OriginatorInfo: " + obj.getClass().getName());
-    }
-    
-    public ASN1Set getCertificates()
-    {
-        return certs;
-    }
-
-    public ASN1Set getCRLs()
-    {
-        return crls;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorInfo ::= SEQUENCE {
-     *     certs [0] IMPLICIT CertificateSet OPTIONAL,
-     *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        if (certs != null)
-        {
-            v.add(new DERTaggedObject(false, 0, certs));
-        }
-        
-        if (crls != null)
-        {
-            v.add(new DERTaggedObject(false, 1, crls));
-        }
-        
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java b/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
deleted file mode 100644
index 826761d..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-
-public class OriginatorPublicKey
-    extends ASN1Encodable
-{
-    private AlgorithmIdentifier algorithm;
-    private DERBitString        publicKey;
-    
-    public OriginatorPublicKey(
-        AlgorithmIdentifier algorithm,
-        byte[]              publicKey)
-    {
-        this.algorithm = algorithm;
-        this.publicKey = new DERBitString(publicKey);
-    }
-    
-    public OriginatorPublicKey(
-        ASN1Sequence seq)
-    {
-        algorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
-        publicKey = (DERBitString)seq.getObjectAt(1);
-    }
-    
-    /**
-     * return an OriginatorPublicKey object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static OriginatorPublicKey getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return an OriginatorPublicKey object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static OriginatorPublicKey getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof OriginatorPublicKey)
-        {
-            return (OriginatorPublicKey)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new OriginatorPublicKey((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid OriginatorPublicKey: " + obj.getClass().getName());
-    } 
-
-    public AlgorithmIdentifier getAlgorithm()
-    {
-        return algorithm;
-    }
-
-    public DERBitString getPublicKey()
-    {
-        return publicKey;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorPublicKey ::= SEQUENCE {
-     *     algorithm AlgorithmIdentifier,
-     *     publicKey BIT STRING 
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(algorithm);
-        v.add(publicKey);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java b/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
deleted file mode 100644
index 1232363..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class OtherKeyAttribute
-    extends ASN1Encodable
-{
-    private DERObjectIdentifier keyAttrId;
-    private DEREncodable        keyAttr;
-
-    /**
-     * return an OtherKeyAttribute object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static OtherKeyAttribute getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof OtherKeyAttribute)
-        {
-            return (OtherKeyAttribute)o;
-        }
-        
-        if (o instanceof ASN1Sequence)
-        {
-            return new OtherKeyAttribute((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-    
-    public OtherKeyAttribute(
-        ASN1Sequence seq)
-    {
-        keyAttrId = (DERObjectIdentifier)seq.getObjectAt(0);
-        keyAttr = seq.getObjectAt(1);
-    }
-
-    public OtherKeyAttribute(
-        DERObjectIdentifier keyAttrId,
-        DEREncodable        keyAttr)
-    {
-        this.keyAttrId = keyAttrId;
-        this.keyAttr = keyAttr;
-    }
-
-    public DERObjectIdentifier getKeyAttrId()
-    {
-        return keyAttrId;
-    }
-    
-    public DEREncodable getKeyAttr()
-    {
-        return keyAttr;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OtherKeyAttribute ::= SEQUENCE {
-     *     keyAttrId OBJECT IDENTIFIER,
-     *     keyAttr ANY DEFINED BY keyAttrId OPTIONAL
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(keyAttrId);
-        v.add(keyAttr);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
deleted file mode 100644
index e70c43c..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class OtherRecipientInfo
-    extends ASN1Encodable
-{
-    private DERObjectIdentifier    oriType;
-    private DEREncodable           oriValue;
-
-    public OtherRecipientInfo(
-        DERObjectIdentifier     oriType,
-        DEREncodable            oriValue)
-    {
-        this.oriType = oriType;
-        this.oriValue = oriValue;
-    }
-    
-    public OtherRecipientInfo(
-        ASN1Sequence seq)
-    {
-        oriType = DERObjectIdentifier.getInstance(seq.getObjectAt(1));
-        oriValue = seq.getObjectAt(2);
-    }
-
-    /**
-     * return a OtherRecipientInfo object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static OtherRecipientInfo getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return a OtherRecipientInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static OtherRecipientInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof OtherRecipientInfo)
-        {
-            return (OtherRecipientInfo)obj;
-        }
-        
-        if (obj instanceof ASN1Sequence)
-        {
-            return new OtherRecipientInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid OtherRecipientInfo: " + obj.getClass().getName());
-    }
-
-    public DERObjectIdentifier getType()
-    {
-        return oriType;
-    }
-
-    public DEREncodable getValue()
-    {
-        return oriValue;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OtherRecipientInfo ::= SEQUENCE {
-     *    oriType OBJECT IDENTIFIER,
-     *    oriValue ANY DEFINED BY oriType }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(oriType);
-        v.add(oriValue);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
deleted file mode 100644
index 555a820..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class PasswordRecipientInfo
-    extends ASN1Encodable
-{
-    private DERInteger          version;
-    private AlgorithmIdentifier keyDerivationAlgorithm;
-    private AlgorithmIdentifier keyEncryptionAlgorithm;
-    private ASN1OctetString     encryptedKey;
-
-    public PasswordRecipientInfo(
-        AlgorithmIdentifier     keyEncryptionAlgorithm,
-        ASN1OctetString         encryptedKey)
-    {
-        this.version = new DERInteger(0);
-        this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-        this.encryptedKey = encryptedKey;
-    }
-    
-    public PasswordRecipientInfo(
-        AlgorithmIdentifier     keyDerivationAlgorithm,
-        AlgorithmIdentifier     keyEncryptionAlgorithm,
-        ASN1OctetString         encryptedKey)
-    {
-        this.version = new DERInteger(0);
-        this.keyDerivationAlgorithm = keyDerivationAlgorithm;
-        this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-        this.encryptedKey = encryptedKey;
-    }
-    
-    public PasswordRecipientInfo(
-        ASN1Sequence seq)
-    {
-        version = (DERInteger)seq.getObjectAt(0);
-        if (seq.getObjectAt(1) instanceof ASN1TaggedObject)
-        {
-            keyDerivationAlgorithm = AlgorithmIdentifier.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
-            keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(2));
-            encryptedKey = (ASN1OctetString)seq.getObjectAt(3);
-        }
-        else
-        {
-            keyEncryptionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-            encryptedKey = (ASN1OctetString)seq.getObjectAt(2);
-        }
-    }
-
-    /**
-     * return a PasswordRecipientInfo object from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want.
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static PasswordRecipientInfo getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-    
-    /**
-     * return a PasswordRecipientInfo object from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static PasswordRecipientInfo getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof PasswordRecipientInfo)
-        {
-            return (PasswordRecipientInfo)obj;
-        }
-        
-        if(obj instanceof ASN1Sequence)
-        {
-            return new PasswordRecipientInfo((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid PasswordRecipientInfo: " + obj.getClass().getName());
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public AlgorithmIdentifier getKeyDerivationAlgorithm()
-    {
-        return keyDerivationAlgorithm;
-    }
-
-    public AlgorithmIdentifier getKeyEncryptionAlgorithm()
-    {
-        return keyEncryptionAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedKey()
-    {
-        return encryptedKey;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * PasswordRecipientInfo ::= SEQUENCE {
-     *   version CMSVersion,   -- Always set to 0
-     *   keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
-     *                             OPTIONAL,
-     *  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *  encryptedKey EncryptedKey }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        
-        if (keyDerivationAlgorithm != null)
-        {
-            v.add(new DERTaggedObject(false, 0, keyDerivationAlgorithm));
-        }
-        v.add(keyEncryptionAlgorithm);
-        v.add(encryptedKey);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java b/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
deleted file mode 100644
index 2578583..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class RecipientIdentifier
-    extends ASN1Encodable
-{
-    private DEREncodable id;
-    
-    public RecipientIdentifier(
-        IssuerAndSerialNumber id)
-    {
-        this.id = id;
-    }
-    
-    public RecipientIdentifier(
-        ASN1OctetString id)
-    {
-        this.id = new DERTaggedObject(false, 0, id);
-    }
-    
-    public RecipientIdentifier(
-        DERObject id)
-    {
-        this.id = id;
-    }
-    
-    /**
-     * return a RecipientIdentifier object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static RecipientIdentifier getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof RecipientIdentifier)
-        {
-            return (RecipientIdentifier)o;
-        }
-        
-        if (o instanceof IssuerAndSerialNumber)
-        {
-            return new RecipientIdentifier((IssuerAndSerialNumber)o);
-        }
-        
-        if (o instanceof ASN1OctetString)
-        {
-            return new RecipientIdentifier((ASN1OctetString)o);
-        }
-        
-        if (o instanceof DERObject)
-        {
-            return new RecipientIdentifier((DERObject)o);
-        }
-        
-        throw new IllegalArgumentException(
-          "Illegal object in RecipientIdentifier: " + o.getClass().getName());
-    } 
-
-    public boolean isTagged()
-    {
-        return (id instanceof ASN1TaggedObject);
-    }
-
-    public DEREncodable getId()
-    {
-        if (id instanceof ASN1TaggedObject)
-        {
-            return ASN1OctetString.getInstance((ASN1TaggedObject)id, false);
-        }
-
-        return IssuerAndSerialNumber.getInstance(id);
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientIdentifier ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return id.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java b/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
deleted file mode 100644
index 13f98f2..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class RecipientInfo
-    extends ASN1Encodable
-{
-    DEREncodable    info;
-
-    public RecipientInfo(
-        KeyTransRecipientInfo info)
-    {
-        this.info = info;
-    }
-
-    public RecipientInfo(
-        KeyAgreeRecipientInfo info)
-    {
-        this.info = new DERTaggedObject(true, 1, info);
-    }
-
-    public RecipientInfo(
-        KEKRecipientInfo info)
-    {
-        this.info = new DERTaggedObject(true, 2, info);
-    }
-
-    public RecipientInfo(
-        PasswordRecipientInfo info)
-    {
-        this.info = new DERTaggedObject(true, 3, info);
-    }
-
-    public RecipientInfo(
-        OtherRecipientInfo info)
-    {
-        this.info = new DERTaggedObject(true, 4, info);
-    }
-
-    public RecipientInfo(
-        DERObject   info)
-    {
-        this.info = info;
-    }
-
-    public static RecipientInfo getInstance(
-        Object  o)
-    {
-        if (o == null || o instanceof RecipientInfo)
-        {
-            return (RecipientInfo)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new RecipientInfo((ASN1Sequence)o);
-        }
-        else if (o instanceof ASN1TaggedObject)
-        {
-            return new RecipientInfo((ASN1TaggedObject)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory: "
-                                                    + o.getClass().getName());
-    }
-
-    public DERInteger getVersion()
-    {
-        if (info instanceof ASN1TaggedObject)
-        {
-            ASN1TaggedObject o = (ASN1TaggedObject)info;
-
-            switch (o.getTagNo())
-            {
-            case 1:
-                return KeyAgreeRecipientInfo.getInstance(o, true).getVersion();
-            case 2:
-                return KEKRecipientInfo.getInstance(o, true).getVersion();
-            case 3:
-                return PasswordRecipientInfo.getInstance(o, true).getVersion();
-            case 4:
-                return new DERInteger(0);    // no syntax version for OtherRecipientInfo
-            default:
-                throw new IllegalStateException("unknown tag");
-            }
-        }
-
-        return KeyTransRecipientInfo.getInstance(info).getVersion();
-    }
-
-    public boolean isTagged()
-    {
-        return (info instanceof ASN1TaggedObject);
-    }
-
-    public DEREncodable getInfo()
-    {
-        if (info instanceof ASN1TaggedObject)
-        {
-            ASN1TaggedObject o = (ASN1TaggedObject)info;
-
-            switch (o.getTagNo())
-            {
-            case 1:
-                return KeyAgreeRecipientInfo.getInstance(o, true);
-            case 2:
-                return KEKRecipientInfo.getInstance(o, true);
-            case 3:
-                return PasswordRecipientInfo.getInstance(o, true);
-            case 4:
-                return OtherRecipientInfo.getInstance(o, true);
-            default:
-                throw new IllegalStateException("unknown tag");
-            }
-        }
-
-        return KeyTransRecipientInfo.getInstance(info);
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientInfo ::= CHOICE {
-     *     ktri KeyTransRecipientInfo,
-     *     kari [1] KeyAgreeRecipientInfo,
-     *     kekri [2] KEKRecipientInfo,
-     *     pwri [3] PasswordRecipientInfo,
-     *     ori [4] OtherRecipientInfo }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return info.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java b/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
deleted file mode 100644
index f7e3b19..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-public class RecipientKeyIdentifier
-    extends ASN1Encodable
-{
-    private ASN1OctetString      subjectKeyIdentifier;
-    private DERGeneralizedTime   date;
-    private OtherKeyAttribute    other;
-
-    public RecipientKeyIdentifier(
-        ASN1OctetString         subjectKeyIdentifier,
-        DERGeneralizedTime      date,
-        OtherKeyAttribute       other)
-    {
-        this.subjectKeyIdentifier = subjectKeyIdentifier;
-        this.date = date;
-        this.other = other;
-    }
-    
-    public RecipientKeyIdentifier(
-        ASN1Sequence seq)
-    {
-        subjectKeyIdentifier = ASN1OctetString.getInstance(
-                                                    seq.getObjectAt(0));
-        
-        switch(seq.size())
-        {
-        case 1:
-            break;
-        case 2:
-            if (seq.getObjectAt(1) instanceof DERGeneralizedTime)
-            {
-                date = (DERGeneralizedTime)seq.getObjectAt(1); 
-            }
-            else
-            {
-                other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
-            }
-            break;
-        case 3:
-            date  = (DERGeneralizedTime)seq.getObjectAt(1);
-            other = OtherKeyAttribute.getInstance(seq.getObjectAt(2));
-            break;
-        default:
-            throw new IllegalArgumentException("Invalid KEKIdentifier");
-        }
-    }
-
-    /**
-     * return a RecipientKeyIdentifier object from a tagged object.
-     *
-     * @param _ato the tagged object holding the object we want.
-     * @param _explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the object held by the
-     *          tagged object cannot be converted.
-     */
-    public static RecipientKeyIdentifier getInstance(ASN1TaggedObject _ato, boolean _explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
-    }
-    
-    /**
-     * return a RecipientKeyIdentifier object from the given object.
-     *
-     * @param _obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static RecipientKeyIdentifier getInstance(Object _obj)
-    {
-        if(_obj == null || _obj instanceof RecipientKeyIdentifier)
-        {
-            return (RecipientKeyIdentifier)_obj;
-        }
-        
-        if(_obj instanceof ASN1Sequence)
-        {
-            return new RecipientKeyIdentifier((ASN1Sequence)_obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid RecipientKeyIdentifier: " + _obj.getClass().getName());
-    } 
-
-    public ASN1OctetString getSubjectKeyIdentifier()
-    {
-        return subjectKeyIdentifier;
-    }
-
-    public DERGeneralizedTime getDate()
-    {
-        return date;
-    }
-
-    public OtherKeyAttribute getOtherKeyAttribute()
-    {
-        return other;
-    }
-
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientKeyIdentifier ::= SEQUENCE {
-     *     subjectKeyIdentifier SubjectKeyIdentifier,
-     *     date GeneralizedTime OPTIONAL,
-     *     other OtherKeyAttribute OPTIONAL 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(subjectKeyIdentifier);
-        
-        if (date != null)
-        {
-            v.add(date);
-        }
-
-        if (other != null)
-        {
-            v.add(other);
-        }
-        
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/SignedData.java b/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
deleted file mode 100644
index 434395c..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
+++ /dev/null
@@ -1,215 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.BERSequence;
-import org.bouncycastle.asn1.BERTaggedObject;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-/**
- * a signed data object.
- */
-public class SignedData
-    extends ASN1Encodable
-{
-    private DERInteger  version;
-    private ASN1Set     digestAlgorithms;
-    private ContentInfo contentInfo;
-    private ASN1Set     certificates;
-    private ASN1Set     crls;
-    private ASN1Set     signerInfos;
-    private boolean        certBer;
-    private boolean        crlsBer;
-
-    public static SignedData getInstance(
-        Object  o)
-    {
-        if (o instanceof SignedData)
-        {
-            return (SignedData)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new SignedData((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public SignedData(
-        ASN1Set     digestAlgorithms,
-        ContentInfo contentInfo,
-        ASN1Set     certificates,
-        ASN1Set     crls,
-        ASN1Set     signerInfos)
-    {
-        if (contentInfo.getContentType().equals(CMSObjectIdentifiers.data))
-        {
-            //
-            // we should also be looking for attribute certificates here,
-            // later.
-            //
-            Enumeration e = signerInfos.getObjects();
-            boolean     v3Found = false;
-
-            while (e.hasMoreElements())
-            {
-                SignerInfo  s = SignerInfo.getInstance(e.nextElement());
-
-                if (s.getVersion().getValue().intValue() == 3)
-                {
-                    v3Found = true;
-                }
-            }
-
-            if (v3Found)
-            {
-                this.version = new DERInteger(3);
-            }
-            else
-            {
-                this.version = new DERInteger(1);
-            }
-        }
-        else
-        {
-            this.version = new DERInteger(3);
-        }
-
-        this.digestAlgorithms = digestAlgorithms;
-        this.contentInfo = contentInfo;
-        this.certificates = certificates;
-        this.crls = crls;
-        this.signerInfos = signerInfos;
-    }
-    
-    public SignedData(
-        ASN1Sequence seq)
-    {
-        Enumeration     e = seq.getObjects();
-
-        version = (DERInteger)e.nextElement();
-        digestAlgorithms = ((ASN1Set)e.nextElement());
-        contentInfo = ContentInfo.getInstance(e.nextElement());
-
-        while (e.hasMoreElements())
-        {
-            DERObject o = (DERObject)e.nextElement();
-
-            //
-            // an interesting feature of SignedData is that there appear
-            // to be varying implementations...
-            // for the moment we ignore anything which doesn't fit.
-            //
-            if (o instanceof ASN1TaggedObject)
-            {
-                ASN1TaggedObject tagged = (ASN1TaggedObject)o;
-
-                switch (tagged.getTagNo())
-                {
-                case 0:
-                    certBer = tagged instanceof BERTaggedObject;
-                    certificates = ASN1Set.getInstance(tagged, false);
-                    break;
-                case 1:
-                    crlsBer = tagged instanceof BERTaggedObject;
-                    crls = ASN1Set.getInstance(tagged, false);
-                    break;
-                default:
-                    throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
-                }
-            }
-            else
-            {
-                signerInfos = (ASN1Set)o;
-            }
-        }
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public ASN1Set getDigestAlgorithms()
-    {
-        return digestAlgorithms;
-    }
-
-    public ContentInfo getEncapContentInfo()
-    {
-        return contentInfo;
-    }
-
-    public ASN1Set getCertificates()
-    {
-        return certificates;
-    }
-
-    public ASN1Set getCRLs()
-    {
-        return crls;
-    }
-
-    public ASN1Set getSignerInfos()
-    {
-        return signerInfos;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * SignedData ::= SEQUENCE {
-     *     version CMSVersion,
-     *     digestAlgorithms DigestAlgorithmIdentifiers,
-     *     encapContentInfo EncapsulatedContentInfo,
-     *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
-     *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
-     *     signerInfos SignerInfos
-     *   }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(digestAlgorithms);
-        v.add(contentInfo);
-
-        if (certificates != null)
-        {
-            if (certBer)
-            {
-                v.add(new BERTaggedObject(false, 0, certificates));
-            }
-            else
-            {
-                v.add(new DERTaggedObject(false, 0, certificates));
-            }
-        }
-
-        if (crls != null)
-        {
-            if (crlsBer)
-            {
-                v.add(new BERTaggedObject(false, 1, crls));
-            }
-            else
-            {
-                v.add(new DERTaggedObject(false, 1, crls));
-            }
-        }
-
-        v.add(signerInfos);
-
-        return new BERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java b/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
deleted file mode 100644
index 41b8c57..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class SignerIdentifier
-    extends ASN1Encodable
-{
-    private DEREncodable id;
-    
-    public SignerIdentifier(
-        IssuerAndSerialNumber id)
-    {
-        this.id = id;
-    }
-    
-    public SignerIdentifier(
-        ASN1OctetString id)
-    {
-        this.id = new DERTaggedObject(false, 0, id);
-    }
-    
-    public SignerIdentifier(
-        DERObject id)
-    {
-        this.id = id;
-    }
-    
-    /**
-     * return a SignerIdentifier object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static SignerIdentifier getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof SignerIdentifier)
-        {
-            return (SignerIdentifier)o;
-        }
-        
-        if (o instanceof IssuerAndSerialNumber)
-        {
-            return new SignerIdentifier((IssuerAndSerialNumber)o);
-        }
-        
-        if (o instanceof ASN1OctetString)
-        {
-            return new SignerIdentifier((ASN1OctetString)o);
-        }
-        
-        if (o instanceof DERObject)
-        {
-            return new SignerIdentifier((DERObject)o);
-        }
-        
-        throw new IllegalArgumentException(
-             "Illegal object in SignerIdentifier: " + o.getClass().getName());
-    } 
-
-    public boolean isTagged()
-    {
-        return (id instanceof ASN1TaggedObject);
-    }
-
-    public DEREncodable getId()
-    {
-        if (id instanceof ASN1TaggedObject)
-        {
-            return ASN1OctetString.getInstance((ASN1TaggedObject)id, false);
-        }
-
-        return id;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * SignerIdentifier ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return id.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java b/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
deleted file mode 100644
index 83a1f53..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.*;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class SignerInfo
-    extends ASN1Encodable
-{
-    private DERInteger              version;
-    private SignerIdentifier        sid;
-    private AlgorithmIdentifier     digAlgorithm;
-    private ASN1Set                 authenticatedAttributes;
-    private AlgorithmIdentifier     digEncryptionAlgorithm;
-    private ASN1OctetString         encryptedDigest;
-    private ASN1Set                 unauthenticatedAttributes;
-
-    public static SignerInfo getInstance(
-        Object  o)
-    {
-        if (o == null || o instanceof SignerInfo)
-        {
-            return (SignerInfo)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new SignerInfo((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public SignerInfo(
-        SignerIdentifier        sid,
-        AlgorithmIdentifier     digAlgorithm,
-        ASN1Set                 authenticatedAttributes,
-        AlgorithmIdentifier     digEncryptionAlgorithm,
-        ASN1OctetString         encryptedDigest,
-        ASN1Set                 unauthenticatedAttributes)
-    {
-        if (sid.isTagged())
-        {
-            this.version = new DERInteger(3);
-        }
-        else
-        {
-            this.version = new DERInteger(1);
-        }
-
-        this.sid = sid;
-        this.digAlgorithm = digAlgorithm;
-        this.authenticatedAttributes = authenticatedAttributes;
-        this.digEncryptionAlgorithm = digEncryptionAlgorithm;
-        this.encryptedDigest = encryptedDigest;
-        this.unauthenticatedAttributes = unauthenticatedAttributes;
-    }
-
-    public SignerInfo(
-        ASN1Sequence seq)
-    {
-        Enumeration     e = seq.getObjects();
-
-        version = (DERInteger)e.nextElement();
-        sid = SignerIdentifier.getInstance(e.nextElement());
-        digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
-
-        Object obj = e.nextElement();
-
-        if (obj instanceof ASN1TaggedObject)
-        {
-            authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false);
-
-            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
-        }
-        else
-        {
-            authenticatedAttributes = null;
-            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj);
-        }
-
-        encryptedDigest = DEROctetString.getInstance(e.nextElement());
-
-        if (e.hasMoreElements())
-        {
-            unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
-        }
-        else
-        {
-            unauthenticatedAttributes = null;
-        }
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public SignerIdentifier getSID()
-    {
-        return sid;
-    }
-
-    public ASN1Set getAuthenticatedAttributes()
-    {
-        return authenticatedAttributes;
-    }
-
-    public AlgorithmIdentifier getDigestAlgorithm()
-    {
-        return digAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedDigest()
-    {
-        return encryptedDigest;
-    }
-
-    public AlgorithmIdentifier getDigestEncryptionAlgorithm()
-    {
-        return digEncryptionAlgorithm;
-    }
-
-    public ASN1Set getUnauthenticatedAttributes()
-    {
-        return unauthenticatedAttributes;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     *  SignerInfo ::= SEQUENCE {
-     *      version Version,
-     *      SignerIdentifier sid,
-     *      digestAlgorithm DigestAlgorithmIdentifier,
-     *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
-     *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
-     *      encryptedDigest EncryptedDigest,
-     *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
-     *  }
-     *
-     *  EncryptedDigest ::= OCTET STRING
-     *
-     *  DigestAlgorithmIdentifier ::= AlgorithmIdentifier
-     *
-     *  DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(sid);
-        v.add(digAlgorithm);
-
-        if (authenticatedAttributes != null)
-        {
-            v.add(new DERTaggedObject(false, 0, authenticatedAttributes));
-        }
-
-        v.add(digEncryptionAlgorithm);
-        v.add(encryptedDigest);
-
-        if (unauthenticatedAttributes != null)
-        {
-            v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/Time.java b/src/main/java/org/bouncycastle/asn1/cms/Time.java
deleted file mode 100644
index 6b8817c..0000000
--- a/src/main/java/org/bouncycastle/asn1/cms/Time.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.bouncycastle.asn1.cms;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.SimpleTimeZone;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERUTCTime;
-
-public class Time
-    extends ASN1Encodable
-{
-    DERObject   time;
-
-    public static Time getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(obj.getObject());
-    }
-
-    public Time(
-        DERObject   time)
-    {
-        if (!(time instanceof DERUTCTime)
-            && !(time instanceof DERGeneralizedTime))
-        {
-            throw new IllegalArgumentException("unknown object passed to Time");
-        }
-
-        this.time = time; 
-    }
-
-    /**
-     * creates a time object from a given date - if the date is between 1950
-     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
-     * is used.
-     */
-    public Time(
-        Date    date)
-    {
-        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
-        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss");
-
-        dateF.setTimeZone(tz);
-
-        String  d = dateF.format(date) + "Z";
-        int     year = Integer.parseInt(d.substring(0, 4));
-
-        if (year < 1950 || year > 2049)
-        {
-            time = new DERGeneralizedTime(d);
-        }
-        else
-        {
-            time = new DERUTCTime(d.substring(2));
-        }
-    }
-
-    public static Time getInstance(
-        Object  obj)
-    {
-        if (obj instanceof Time)
-        {
-            return (Time)obj;
-        }
-        else if (obj instanceof DERUTCTime)
-        {
-            return new Time((DERUTCTime)obj);
-        }
-        else if (obj instanceof DERGeneralizedTime)
-        {
-            return new Time((DERGeneralizedTime)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public String getTime()
-    {
-        if (time instanceof DERUTCTime)
-        {
-            return ((DERUTCTime)time).getAdjustedTime();
-        }
-        else
-        {
-            return ((DERGeneralizedTime)time).getTime();
-        }
-    }
-
-    public Date getDate()
-    {
-        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
-
-        return dateF.parse(this.getTime(), new ParsePosition(0));
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Time ::= CHOICE {
-     *             utcTime        UTCTime,
-     *             generalTime    GeneralizedTime }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return time;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java b/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java
deleted file mode 100644
index 90e90d4..0000000
--- a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIdentifier.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.bouncycastle.asn1.esf;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-
-public interface CommitmentTypeIdentifier
-{
-    public static final DERObjectIdentifier proofOfOrigin = PKCSObjectIdentifiers.id_cti_ets_proofOfOrigin;
-    public static final DERObjectIdentifier proofOfReceipt = PKCSObjectIdentifiers.id_cti_ets_proofOfReceipt;
-    public static final DERObjectIdentifier proofOfDelivery = PKCSObjectIdentifiers.id_cti_ets_proofOfDelivery;
-    public static final DERObjectIdentifier proofOfSender = PKCSObjectIdentifiers.id_cti_ets_proofOfSender;
-    public static final DERObjectIdentifier proofOfApproval = PKCSObjectIdentifiers.id_cti_ets_proofOfApproval;
-    public static final DERObjectIdentifier proofOfCreation = PKCSObjectIdentifiers.id_cti_ets_proofOfCreation;
-}
diff --git a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java b/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java
deleted file mode 100644
index 76616ea..0000000
--- a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeIndication.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.bouncycastle.asn1.esf;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class CommitmentTypeIndication
-    extends ASN1Encodable 
-{
-    private DERObjectIdentifier   commitmentTypeId;
-    private ASN1Sequence          commitmentTypeQualifier;
-    
-    public CommitmentTypeIndication(
-        ASN1Sequence seq)
-    {
-        commitmentTypeId = (DERObjectIdentifier)seq.getObjectAt(0);
-
-        if (seq.size() > 1)
-        {
-            commitmentTypeQualifier = (ASN1Sequence)seq.getObjectAt(1);
-        }
-    }
-
-    public CommitmentTypeIndication(
-        DERObjectIdentifier commitmentTypeId)
-    {
-        this.commitmentTypeId = commitmentTypeId;
-    }
-
-    public CommitmentTypeIndication(
-        DERObjectIdentifier commitmentTypeId,
-        ASN1Sequence        commitmentTypeQualifier)
-    {
-        this.commitmentTypeId = commitmentTypeId;
-        this.commitmentTypeQualifier = commitmentTypeQualifier;
-    }
-
-    public static CommitmentTypeIndication getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof CommitmentTypeIndication)
-        {
-            return (CommitmentTypeIndication)obj;
-        }
-
-        return new CommitmentTypeIndication(ASN1Sequence.getInstance(obj));
-    }
-
-    public DERObjectIdentifier getCommitmentTypeId()
-    {
-        return commitmentTypeId;
-    }
-    
-    public ASN1Sequence getCommitmentTypeQualifier()
-    {
-        return commitmentTypeQualifier;
-    }
-    
-    /**
-     * <pre>
-     * CommitmentTypeIndication ::= SEQUENCE {
-     *      commitmentTypeId   CommitmentTypeIdentifier,
-     *      commitmentTypeQualifier   SEQUENCE SIZE (1..MAX) OF
-     *              CommitmentTypeQualifier OPTIONAL }
-     * </pre>
-     */ 
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        v.add(commitmentTypeId);
-
-        if (commitmentTypeQualifier != null)
-        {
-            v.add(commitmentTypeQualifier);
-        }
-        
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java b/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java
deleted file mode 100644
index 7895e76..0000000
--- a/src/main/java/org/bouncycastle/asn1/esf/CommitmentTypeQualifier.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.bouncycastle.asn1.esf;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * Commitment type qualifiers, used in the Commitment-Type-Indication attribute (RFC3126).
- * 
- * <pre>
- *   CommitmentTypeQualifier ::= SEQUENCE {
- *       commitmentTypeIdentifier  CommitmentTypeIdentifier,
- *       qualifier          ANY DEFINED BY commitmentTypeIdentifier OPTIONAL }
- * </pre>
- */
-public class CommitmentTypeQualifier
-    extends ASN1Encodable
-{
-   private DERObjectIdentifier commitmentTypeIdentifier;
-   private DEREncodable qualifier;
-
-   /**
-    * Creates a new <code>CommitmentTypeQualifier</code> instance.
-    *
-    * @param commitmentTypeIdentifier a <code>CommitmentTypeIdentifier</code> value
-    */
-    public CommitmentTypeQualifier(
-        DERObjectIdentifier commitmentTypeIdentifier)
-    {
-        this(commitmentTypeIdentifier, null);
-    }
-    
-   /**
-    * Creates a new <code>CommitmentTypeQualifier</code> instance.
-    *
-    * @param commitmentTypeIdentifier a <code>CommitmentTypeIdentifier</code> value
-    * @param qualifier the qualifier, defined by the above field.
-    */
-    public CommitmentTypeQualifier(
-        DERObjectIdentifier commitmentTypeIdentifier,
-        DEREncodable qualifier) 
-    {
-        this.commitmentTypeIdentifier = commitmentTypeIdentifier;
-        this.qualifier = qualifier;
-    }
-
-    /**
-     * Creates a new <code>CommitmentTypeQualifier</code> instance.
-     *
-     * @param as <code>CommitmentTypeQualifier</code> structure
-     * encoded as an ASN1Sequence. 
-     */
-    public CommitmentTypeQualifier(
-        ASN1Sequence as)
-    {
-        commitmentTypeIdentifier = (DERObjectIdentifier)as.getObjectAt(0);
-        
-        if (as.size() > 1)
-        {
-            qualifier = as.getObjectAt(1);
-        }
-    }
-
-    public static CommitmentTypeQualifier getInstance(Object as)
-    {
-        if (as instanceof CommitmentTypeQualifier || as == null)
-        {
-            return (CommitmentTypeQualifier)as;
-        }
-        else if (as instanceof ASN1Sequence)
-        {
-            return new CommitmentTypeQualifier((ASN1Sequence)as);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance.");
-    }
-
-    public DERObjectIdentifier getCommitmentTypeIdentifier()
-    {
-        return commitmentTypeIdentifier;
-    }
-    
-    public DEREncodable getQualifier()
-    {
-        return qualifier;
-    }
-
-   /**
-    * Returns a DER-encodable representation of this instance. 
-    *
-    * @return a <code>DERObject</code> value
-    */
-   public DERObject toASN1Object() 
-   {
-      ASN1EncodableVector dev = new ASN1EncodableVector();
-      dev.add(commitmentTypeIdentifier);
-      if (qualifier != null)
-      {
-          dev.add(qualifier);
-      }
-
-      return new DERSequence(dev);
-   }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java b/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java
deleted file mode 100644
index 6d652e0..0000000
--- a/src/main/java/org/bouncycastle/asn1/esf/ESFAttributes.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.bouncycastle.asn1.esf;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-
-public interface ESFAttributes
-{
-    public static final DERObjectIdentifier  sigPolicyId = PKCSObjectIdentifiers.id_aa_sigPolicyId;
-    public static final DERObjectIdentifier  commitmentType = PKCSObjectIdentifiers.id_aa_commitmentType;
-    public static final DERObjectIdentifier  signerLocation = PKCSObjectIdentifiers.id_aa_signerLocation;
-}
diff --git a/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java b/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
deleted file mode 100644
index 49bd819..0000000
--- a/src/main/java/org/bouncycastle/asn1/esf/SignerLocation.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.bouncycastle.asn1.esf;
-
-import org.bouncycastle.asn1.*;
-
-import java.util.Enumeration;
-
-/**
- * Signer-Location attribute (RFC3126).
- * 
- * <pre>
- *   SignerLocation ::= SEQUENCE {
- *       countryName        [0] DirectoryString OPTIONAL,
- *       localityName       [1] DirectoryString OPTIONAL,
- *       postalAddress      [2] PostalAddress OPTIONAL }
- *
- *   PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString
- * </pre>
- */
-public class SignerLocation
-    extends ASN1Encodable 
-{
-    private DERUTF8String   countryName;
-    private DERUTF8String   localityName;
-    private ASN1Sequence    postalAddress;
-    
-    public SignerLocation(
-        ASN1Sequence seq)
-    {
-        Enumeration     e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            DERTaggedObject o = (DERTaggedObject)e.nextElement();
-
-            switch (o.getTagNo())
-            {
-            case 0:
-                this.countryName = DERUTF8String.getInstance(o, true);
-                break;
-            case 1:
-                this.localityName = DERUTF8String.getInstance(o, true);
-                break;
-            case 2:
-                this.postalAddress = ASN1Sequence.getInstance(o, true);
-                if (postalAddress != null && postalAddress.size() > 6)
-                {
-                    throw new IllegalArgumentException("postal address must contain less than 6 strings");
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("illegal tag");
-            }
-        }
-    }
-
-    public SignerLocation(
-        DERUTF8String   countryName,
-        DERUTF8String   localityName,
-        ASN1Sequence    postalAddress)
-    {
-        if (postalAddress != null && postalAddress.size() > 6)
-        {
-            throw new IllegalArgumentException("postal address must contain less than 6 strings");
-        }
-
-        if (countryName != null)
-        {
-            this.countryName = DERUTF8String.getInstance(countryName.toASN1Object());
-        }
-
-        if (localityName != null)
-        {
-            this.localityName = DERUTF8String.getInstance(localityName.toASN1Object());
-        }
-
-        if (postalAddress != null)
-        {
-            this.postalAddress = ASN1Sequence.getInstance(postalAddress.toASN1Object());
-        }
-    }
-
-    public static SignerLocation getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof SignerLocation)
-        {
-            return (SignerLocation)obj;
-        }
-
-        return new SignerLocation(ASN1Sequence.getInstance(obj));
-    }
-
-    public DERUTF8String getCountryName()
-    {
-        return countryName;
-    }
-
-    public DERUTF8String getLocalityName()
-    {
-        return localityName;
-    }
-
-    public ASN1Sequence getPostalAddress()
-    {
-        return postalAddress;
-    }
-
-    /**
-     * <pre>
-     *   SignerLocation ::= SEQUENCE {
-     *       countryName        [0] DirectoryString OPTIONAL,
-     *       localityName       [1] DirectoryString OPTIONAL,
-     *       postalAddress      [2] PostalAddress OPTIONAL }
-     *
-     *   PostalAddress ::= SEQUENCE SIZE(1..6) OF DirectoryString
-     *   
-     *   DirectoryString ::= CHOICE {
-     *         teletexString           TeletexString (SIZE (1..MAX)),
-     *         printableString         PrintableString (SIZE (1..MAX)),
-     *         universalString         UniversalString (SIZE (1..MAX)),
-     *         utf8String              UTF8String (SIZE (1.. MAX)),
-     *         bmpString               BMPString (SIZE (1..MAX)) }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        if (countryName != null)
-        {
-            v.add(new DERTaggedObject(true, 0, countryName));
-        }
-
-        if (localityName != null)
-        {
-            v.add(new DERTaggedObject(true, 1, localityName));
-        }
-
-        if (postalAddress != null)
-        {
-            v.add(new DERTaggedObject(true, 2, postalAddress));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java b/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java
deleted file mode 100644
index 88b4f45..0000000
--- a/src/main/java/org/bouncycastle/asn1/ess/ContentIdentifier.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.bouncycastle.asn1.ess;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-
-public class ContentIdentifier
-    extends ASN1Encodable
-{
-     ASN1OctetString value;
-
-    public static ContentIdentifier getInstance(Object o)
-    {
-        if (o == null || o instanceof ContentIdentifier)
-        {
-            return (ContentIdentifier) o;
-        }
-        else if (o instanceof ASN1OctetString)
-        {
-            return new ContentIdentifier((ASN1OctetString) o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'ContentIdentifier' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    /**
-     * Create from OCTET STRING whose octets represent the identifier.
-     */
-    public ContentIdentifier(
-        ASN1OctetString value)
-    {
-        this.value = value;
-    }
-
-    /**
-     * Create from byte array representing the identifier.
-     */
-    public ContentIdentifier(
-        byte[] value)
-    {
-        this(new DEROctetString(value));
-    }
-    
-    public ASN1OctetString getValue()
-    {
-        return value;
-    }
-
-    /**
-     * The definition of ContentIdentifier is
-     * <pre>
-     * ContentIdentifier ::=  OCTET STRING
-     * </pre>
-     * id-aa-contentIdentifier OBJECT IDENTIFIER ::= { iso(1)
-     *  member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
-     *  smime(16) id-aa(2) 7 }
-     */
-    public DERObject toASN1Object()
-    {
-        return value;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java b/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java
deleted file mode 100644
index 69b107a..0000000
--- a/src/main/java/org/bouncycastle/asn1/ess/ESSCertID.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package org.bouncycastle.asn1.ess;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.IssuerSerial;
-
-public class ESSCertID
-    extends ASN1Encodable
-{
-    private ASN1OctetString certHash;
-
-    private IssuerSerial issuerSerial;
-
-    public static ESSCertID getInstance(Object o)
-    {
-        if (o == null || o instanceof ESSCertID)
-        {
-            return (ESSCertID)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new ESSCertID((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'ESSCertID' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    /**
-     * constructor
-     */
-    public ESSCertID(ASN1Sequence seq)
-    {
-        if (seq.size() < 1 || seq.size() > 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: " + seq.size());
-        }
-
-        certHash = ASN1OctetString.getInstance(seq.getObjectAt(0));
- 
-        if (seq.size() > 1)
-        {
-            issuerSerial = IssuerSerial.getInstance(seq.getObjectAt(1));
-        }
-    }
-
-    public ESSCertID(
-        byte[]          hash)
-    {
-        certHash = new DEROctetString(hash);
-    }
-
-    public ESSCertID(
-        byte[]          hash,
-        IssuerSerial    issuerSerial)
-    {
-        this.certHash = new DEROctetString(hash);
-        this.issuerSerial = issuerSerial;
-    }
-
-    public byte[] getCertHash()
-    {
-        return certHash.getOctets();
-    }
-
-    public IssuerSerial getIssuerSerial()
-    {
-        return issuerSerial;
-    }
-
-    /**
-     * <pre>
-     * ESSCertID ::= SEQUENCE {
-     *     certHash Hash, 
-     *     issuerSerial IssuerSerial OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        v.add(certHash);
-        
-        if (issuerSerial != null)
-        {
-            v.add(issuerSerial);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java b/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
deleted file mode 100644
index dcd5d50..0000000
--- a/src/main/java/org/bouncycastle/asn1/ess/OtherCertID.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.bouncycastle.asn1.ess;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.IssuerSerial;
-import org.bouncycastle.asn1.x509.DigestInfo;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class OtherCertID
-    extends ASN1Encodable
-{
-    private ASN1Encodable otherCertHash;
-    private IssuerSerial issuerSerial;
-
-    public static OtherCertID getInstance(Object o)
-    {
-        if (o == null || o instanceof OtherCertID)
-        {
-            return (OtherCertID) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new OtherCertID((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'OtherCertID' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    /**
-     * constructor
-     */
-    public OtherCertID(ASN1Sequence seq)
-    {
-        if (seq.size() < 1 || seq.size() > 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
-        }
-
-        if (seq.getObjectAt(0).getDERObject() instanceof ASN1OctetString)
-        {
-            otherCertHash = ASN1OctetString.getInstance(seq.getObjectAt(0));
-        }
-        else
-        {
-            otherCertHash = DigestInfo.getInstance(seq.getObjectAt(0));
-
-        }
-
-        if (seq.size() > 1)
-        {
-            issuerSerial = new IssuerSerial(ASN1Sequence.getInstance(seq.getObjectAt(1)));
-        }
-    }
-
-    public OtherCertID(
-        AlgorithmIdentifier  algId,
-        byte[]               digest)
-    {
-        this.otherCertHash = new DigestInfo(algId, digest);
-    }
-
-    public OtherCertID(
-        AlgorithmIdentifier  algId,
-        byte[]               digest,
-        IssuerSerial    issuerSerial)
-    {
-        this.otherCertHash = new DigestInfo(algId, digest);
-        this.issuerSerial = issuerSerial;
-    }
-
-    public AlgorithmIdentifier getAlgorithmHash()
-    {
-        if (otherCertHash.getDERObject() instanceof ASN1OctetString)
-        {
-            // SHA-1
-            return new AlgorithmIdentifier("1.3.14.3.2.26");
-        }
-        else
-        {
-            return DigestInfo.getInstance(otherCertHash).getAlgorithmId();
-        }
-    }
-
-    public byte[] getCertHash()
-    {
-        if (otherCertHash.getDERObject() instanceof ASN1OctetString)
-        {
-            // SHA-1
-            return ((ASN1OctetString)otherCertHash.getDERObject()).getOctets();
-        }
-        else
-        {
-            return DigestInfo.getInstance(otherCertHash).getDigest();
-        }
-    }
-
-    public IssuerSerial getIssuerSerial()
-    {
-        return issuerSerial;
-    }
-
-    /**
-     * <pre>
-     * OtherCertID ::= SEQUENCE {
-     *     otherCertHash    OtherHash,
-     *     issuerSerial     IssuerSerial OPTIONAL }
-     *
-     * OtherHash ::= CHOICE {
-     *     sha1Hash     OCTET STRING,
-     *     otherHash    OtherHashAlgAndValue }
-     *
-     * OtherHashAlgAndValue ::= SEQUENCE {
-     *     hashAlgorithm    AlgorithmIdentifier,
-     *     hashValue        OCTET STRING }
-     *
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(otherCertHash);
-
-        if (issuerSerial != null)
-        {
-            v.add(issuerSerial);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java b/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java
deleted file mode 100644
index 4bc12a4..0000000
--- a/src/main/java/org/bouncycastle/asn1/ess/OtherSigningCertificate.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package org.bouncycastle.asn1.ess;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.PolicyInformation;
-
-public class OtherSigningCertificate
-    extends ASN1Encodable
-{
-    ASN1Sequence certs;
-    ASN1Sequence policies;
-
-    public static OtherSigningCertificate getInstance(Object o)
-    {
-        if (o == null || o instanceof OtherSigningCertificate)
-        {
-            return (OtherSigningCertificate) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new OtherSigningCertificate((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'OtherSigningCertificate' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    /**
-     * constructeurs
-     */
-    public OtherSigningCertificate(ASN1Sequence seq)
-    {
-        if (seq.size() < 1 || seq.size() > 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
-        }
-
-        this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0));
-
-        if (seq.size() > 1)
-        {
-            this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1));
-        }
-    }
-
-    public OtherSigningCertificate(
-        OtherCertID otherCertID)
-    {
-        certs = new DERSequence(otherCertID);
-    }
-
-    public OtherCertID[] getCerts()
-    {
-        OtherCertID[] cs = new OtherCertID[certs.size()];
-
-        for (int i = 0; i != certs.size(); i++)
-        {
-            cs[i] = OtherCertID.getInstance(certs.getObjectAt(i));
-        }
-
-        return cs;
-    }
-
-    public PolicyInformation[] getPolicies()
-    {
-        if (policies == null)
-        {
-            return null;
-        }
-
-        PolicyInformation[] ps = new PolicyInformation[policies.size()];
-
-        for (int i = 0; i != policies.size(); i++)
-        {
-            ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i));
-        }
-
-        return ps;
-    }
-
-    /**
-     * The definition of OtherSigningCertificate is
-     * <pre>
-     * OtherSigningCertificate ::=  SEQUENCE {
-     *      certs        SEQUENCE OF OtherCertID,
-     *      policies     SEQUENCE OF PolicyInformation OPTIONAL
-     * }
-     * </pre>
-     * id-aa-otherSigCert OBJECT IDENTIFIER ::= { iso(1)
-     *  member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
-     *  smime(16) id-aa(2) 19 }
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(certs);
-
-        if (policies != null)
-        {
-            v.add(policies);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java b/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java
deleted file mode 100644
index bd3c904..0000000
--- a/src/main/java/org/bouncycastle/asn1/ess/SigningCertificate.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package org.bouncycastle.asn1.ess;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.PolicyInformation;
-
-
-public class SigningCertificate
-    extends ASN1Encodable
-{
-    ASN1Sequence certs;
-    ASN1Sequence policies;
-
-    public static SigningCertificate getInstance(Object o)
-    {
-        if (o == null || o instanceof SigningCertificate)
-        {
-            return (SigningCertificate) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new SigningCertificate((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'SigningCertificate' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    /**
-     * constructeurs
-     */
-    public SigningCertificate(ASN1Sequence seq)
-    {
-        if (seq.size() < 1 || seq.size() > 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
-        }
-        this.certs = ASN1Sequence.getInstance(seq.getObjectAt(0));
-        
-        if (seq.size() > 1)
-        {
-            this.policies = ASN1Sequence.getInstance(seq.getObjectAt(1));
-        }
-    }
-
-    public SigningCertificate(
-        ESSCertID essCertID)
-    {
-        certs = new DERSequence(essCertID);
-    }
-
-    public ESSCertID[] getCerts()
-    {
-        ESSCertID[] cs = new ESSCertID[certs.size()];
-        
-        for (int i = 0; i != certs.size(); i++)
-        {
-            cs[i] = ESSCertID.getInstance(certs.getObjectAt(i));
-        }
-        
-        return cs;
-    }
-    
-    public PolicyInformation[] getPolicies()
-    {
-        if (policies == null)
-        {
-            return null;
-        }
-        
-        PolicyInformation[] ps = new PolicyInformation[policies.size()];
-        
-        for (int i = 0; i != policies.size(); i++)
-        {
-            ps[i] = PolicyInformation.getInstance(policies.getObjectAt(i));
-        }
-        
-        return ps;
-    }
-    
-    /**
-     * The definition of SigningCertificate is
-     * <pre>
-     * SigningCertificate ::=  SEQUENCE {
-     *      certs        SEQUENCE OF ESSCertID,
-     *      policies     SEQUENCE OF PolicyInformation OPTIONAL
-     * }
-     * </pre>
-     * id-aa-signingCertificate OBJECT IDENTIFIER ::= { iso(1)
-     *  member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
-     *  smime(16) id-aa(2) 12 }
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(certs);
-        
-        if (policies != null)
-        {
-            v.add(policies);
-        }
-        
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
deleted file mode 100644
index d375f90..0000000
--- a/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.bouncycastle.asn1.gnu;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public interface GNUObjectIdentifiers
-{
-    public static final DERObjectIdentifier GNU = new DERObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
-    public static final DERObjectIdentifier GnuPG = new DERObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Aegypten)
-    public static final DERObjectIdentifier notation = new DERObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation
-    public static final DERObjectIdentifier pkaAddress = new DERObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress
-    public static final DERObjectIdentifier GnuRadar = new DERObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar
-    public static final DERObjectIdentifier digestAlgorithm = new DERObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm
-    public static final DERObjectIdentifier Tiger_192 = new DERObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192
-    public static final DERObjectIdentifier encryptionAlgorithm = new DERObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm
-    public static final DERObjectIdentifier Serpent = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent
-    public static final DERObjectIdentifier Serpent_128_ECB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB
-    public static final DERObjectIdentifier Serpent_128_CBC = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC
-    public static final DERObjectIdentifier Serpent_128_OFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB
-    public static final DERObjectIdentifier Serpent_128_CFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB
-    public static final DERObjectIdentifier Serpent_192_ECB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB
-    public static final DERObjectIdentifier Serpent_192_CBC = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC
-    public static final DERObjectIdentifier Serpent_192_OFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB
-    public static final DERObjectIdentifier Serpent_192_CFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB
-    public static final DERObjectIdentifier Serpent_256_ECB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB
-    public static final DERObjectIdentifier Serpent_256_CBC = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC
-    public static final DERObjectIdentifier Serpent_256_OFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB
-    public static final DERObjectIdentifier Serpent_256_CFB = new DERObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB
-    public static final DERObjectIdentifier CRC = new DERObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
-    public static final DERObjectIdentifier CRC32 = new DERObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
-}
diff --git a/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java b/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java
deleted file mode 100644
index 3413126..0000000
--- a/src/main/java/org/bouncycastle/asn1/icao/DataGroupHash.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.bouncycastle.asn1.icao;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The DataGroupHash object.
- * <pre>
- * DataGroupHash  ::=  SEQUENCE {
- *      dataGroupNumber         DataGroupNumber,
- *      dataGroupHashValue     OCTET STRING }
- * 
- * DataGroupNumber ::= INTEGER {
- *         dataGroup1    (1),
- *         dataGroup1    (2),
- *         dataGroup1    (3),
- *         dataGroup1    (4),
- *         dataGroup1    (5),
- *         dataGroup1    (6),
- *         dataGroup1    (7),
- *         dataGroup1    (8),
- *         dataGroup1    (9),
- *         dataGroup1    (10),
- *         dataGroup1    (11),
- *         dataGroup1    (12),
- *         dataGroup1    (13),
- *         dataGroup1    (14),
- *         dataGroup1    (15),
- *         dataGroup1    (16) }
- * 
- * </pre>
- */
-public class DataGroupHash 
-    extends ASN1Encodable
-{
-    DERInteger dataGroupNumber;    
-    ASN1OctetString    dataGroupHashValue;
-    
-    public static DataGroupHash getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof DataGroupHash)
-        {
-            return (DataGroupHash)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new DataGroupHash(ASN1Sequence.getInstance(obj));            
-        }
-        else
-        {
-            throw new IllegalArgumentException("unknown object in getInstance");
-        }
-    }                
-            
-    public DataGroupHash(ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        // dataGroupNumber
-        dataGroupNumber = DERInteger.getInstance(e.nextElement());
-        // dataGroupHashValue
-        dataGroupHashValue = ASN1OctetString.getInstance(e.nextElement());   
-    }
-    
-    public DataGroupHash(
-        int dataGroupNumber,        
-        ASN1OctetString     dataGroupHashValue)
-    {
-        this.dataGroupNumber = new DERInteger(dataGroupNumber);
-        this.dataGroupHashValue = dataGroupHashValue; 
-    }    
-
-    public int getDataGroupNumber()
-    {
-        return dataGroupNumber.getValue().intValue();
-    }
-    
-    public ASN1OctetString getDataGroupHashValue()
-    {
-        return dataGroupHashValue;
-    }     
-    
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        seq.add(dataGroupNumber);
-        seq.add(dataGroupHashValue);  
-
-        return new DERSequence(seq);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
deleted file mode 100644
index 957993a..0000000
--- a/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.bouncycastle.asn1.icao;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public interface ICAOObjectIdentifiers
-{
-    //
-    // base id
-    //
-    static final String                 id_icao                   = "1.3.27";
-
-    static final DERObjectIdentifier    id_icao_mrtd              = new DERObjectIdentifier(id_icao+".1");
-    static final DERObjectIdentifier    id_icao_mrtd_security     = new DERObjectIdentifier(id_icao_mrtd+".1");
-    static final DERObjectIdentifier    id_icao_ldsSecurityObject = new DERObjectIdentifier(id_icao_mrtd_security+".1");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java b/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
deleted file mode 100644
index 177fdcf..0000000
--- a/src/main/java/org/bouncycastle/asn1/icao/LDSSecurityObject.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package org.bouncycastle.asn1.icao;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-/**
- * The LDSSecurityObject object.
- * <pre>
- * LDSSecurityObject ::= SEQUENCE {
- *   version                LDSSecurityObjectVersion,
- *   hashAlgorithm          DigestAlgorithmIdentifier,
- *   dataGroupHashValues    SEQUENCE SIZE (2..ub-DataGroups) OF DataHashGroup}
- *   
- * DigestAlgorithmIdentifier ::= AlgorithmIdentifier,
- * 
- * LDSSecurityObjectVersion :: INTEGER {V0(0)}
- * </pre>
- */
-
-public class LDSSecurityObject 
-    extends ASN1Encodable 
-    implements ICAOObjectIdentifiers    
-{
-    
-    public static final int ub_DataGroups = 16;
-    
-    DERInteger version = new DERInteger(0);
-    AlgorithmIdentifier digestAlgorithmIdentifier; 
-    DataGroupHash[] datagroupHash;            
-
-    public static LDSSecurityObject getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof LDSSecurityObject)
-        {
-            return (LDSSecurityObject)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new LDSSecurityObject(ASN1Sequence.getInstance(obj));            
-        }
-        
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }    
-    
-    public LDSSecurityObject(
-        ASN1Sequence seq)
-    {
-        if (seq == null || seq.size() == 0)
-        {
-            throw new IllegalArgumentException("null or empty sequence passed.");
-        }
-        
-        Enumeration e = seq.getObjects();
-
-        // version
-        version = DERInteger.getInstance(e.nextElement());
-        // digestAlgorithmIdentifier
-        digestAlgorithmIdentifier = AlgorithmIdentifier.getInstance(e.nextElement());
-      
-        ASN1Sequence datagroupHashSeq = ASN1Sequence.getInstance(e.nextElement());
-
-        checkDatagroupHashSeqSize(datagroupHashSeq.size());        
-        
-        datagroupHash = new DataGroupHash[datagroupHashSeq.size()];
-        for (int i= 0; i< datagroupHashSeq.size();i++)
-        {
-            datagroupHash[i] = DataGroupHash.getInstance(datagroupHashSeq.getObjectAt(i));
-        } 
-        
-    }
-
-    public LDSSecurityObject(
-        AlgorithmIdentifier digestAlgorithmIdentifier, 
-        DataGroupHash[]       datagroupHash)
-    {
-        this.digestAlgorithmIdentifier = digestAlgorithmIdentifier;
-        this.datagroupHash = datagroupHash;
-        
-        checkDatagroupHashSeqSize(datagroupHash.length);                      
-    }    
-        
-    private void checkDatagroupHashSeqSize(int size)
-    {
-        if ((size < 2) || (size > ub_DataGroups))
-        {
-               throw new IllegalArgumentException("wrong size in DataGroupHashValues : not in (2.."+ ub_DataGroups +")");
-        }
-    }  
-    
-    public AlgorithmIdentifier getDigestAlgorithmIdentifier()
-    {
-        return digestAlgorithmIdentifier;
-    }
-    
-    public DataGroupHash[] getDatagroupHash()
-    {
-        return datagroupHash;
-    }
-
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        
-        seq.add(version);
-        seq.add(digestAlgorithmIdentifier);
-                
-        ASN1EncodableVector seqname = new ASN1EncodableVector();
-        for (int i = 0; i < datagroupHash.length; i++) 
-        {
-            seqname.add(datagroupHash[i]);
-        }            
-        seq.add(new DERSequence(seqname));                   
-        
-        return new DERSequence(seq);
-    }          
-}
diff --git a/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
new file mode 100644
index 0000000..c1b2356
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.asn1.isismtt;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+
+public interface ISISMTTObjectIdentifiers
+{
+
+    public static final DERObjectIdentifier id_isismtt = new DERObjectIdentifier("1.3.36.8");
+
+    public static final DERObjectIdentifier id_isismtt_cp = new DERObjectIdentifier(id_isismtt + ".1");
+
+    /**
+     * The id-isismtt-cp-accredited OID indicates that the certificate is a
+     * qualified certificate according to Directive 1999/93/EC of the European
+     * Parliament and of the Council of 13 December 1999 on a Community
+     * Framework for Electronic Signatures, which additionally conforms the
+     * special requirements of the SigG and has been issued by an accredited CA.
+     */
+    public static final DERObjectIdentifier id_isismtt_cp_accredited = new DERObjectIdentifier(id_isismtt_cp + ".1");
+
+    public static final DERObjectIdentifier id_isismtt_at = new DERObjectIdentifier(id_isismtt + ".3");
+
+    /**
+     * Certificate extensionDate of certificate generation
+     * 
+     * <pre>
+     *                DateOfCertGenSyntax ::= GeneralizedTime
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_dateOfCertGen = new DERObjectIdentifier(id_isismtt_at + ".1");
+
+    /**
+     * Attribute to indicate that the certificate holder may sign in the name of
+     * a third person. May also be used as extension in a certificate.
+     */
+    public static final DERObjectIdentifier id_isismtt_at_procuration = new DERObjectIdentifier(id_isismtt_at + ".2");
+
+    /**
+     * Attribute to indicate admissions to certain professions. May be used as
+     * attribute in attribute certificate or as extension in a certificate
+     */
+    public static final DERObjectIdentifier id_isismtt_at_admission = new DERObjectIdentifier(id_isismtt_at + ".3");
+
+    /**
+     * Monetary limit for transactions. The QcEuMonetaryLimit QC statement MUST
+     * be used in new certificates in place of the extension/attribute
+     * MonetaryLimit since January 1, 2004. For the sake of backward
+     * compatibility with certificates already in use, SigG conforming
+     * components MUST support MonetaryLimit (as well as QcEuLimitValue).
+     */
+    public static final DERObjectIdentifier id_isismtt_at_monetaryLimit = new DERObjectIdentifier(id_isismtt_at + ".4");
+
+    /**
+     * A declaration of majority. May be used as attribute in attribute
+     * certificate or as extension in a certificate
+     */
+    public static final DERObjectIdentifier id_isismtt_at_declarationOfMajority = new DERObjectIdentifier(id_isismtt_at + ".5");
+
+    /**
+     * 
+     * Serial number of the smart card containing the corresponding private key
+     * 
+     * <pre>
+     *                 ICCSNSyntax ::= OCTET STRING (SIZE(8..20))
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_iCCSN = new DERObjectIdentifier(id_isismtt_at + ".6");
+
+    /**
+     * 
+     * Reference for a file of a smartcard that stores the public key of this
+     * certificate and that is used as �security anchor�.
+     * 
+     * <pre>
+     *      PKReferenceSyntax ::= OCTET STRING (SIZE(20))
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_PKReference = new DERObjectIdentifier(id_isismtt_at + ".7");
+
+    /**
+     * Some other restriction regarding the usage of this certificate. May be
+     * used as attribute in attribute certificate or as extension in a
+     * certificate.
+     * 
+     * <pre>
+     *             RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+     * </pre>
+     * 
+     * @see org.bouncycastle.asn1.isismtt.x509.Restriction
+     */
+    public static final DERObjectIdentifier id_isismtt_at_restriction = new DERObjectIdentifier(id_isismtt_at + ".8");
+
+    /**
+     * 
+     * (Single)Request extension: Clients may include this extension in a
+     * (single) Request to request the responder to send the certificate in the
+     * response message along with the status information. Besides the LDAP
+     * service, this extension provides another mechanism for the distribution
+     * of certificates, which MAY optionally be provided by certificate
+     * repositories.
+     * 
+     * <pre>
+     *        RetrieveIfAllowed ::= BOOLEAN
+     *       
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_retrieveIfAllowed = new DERObjectIdentifier(id_isismtt_at + ".9");
+
+    /**
+     * SingleOCSPResponse extension: The certificate requested by the client by
+     * inserting the RetrieveIfAllowed extension in the request, will be
+     * returned in this extension.
+     * 
+     * @see org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
+     */
+    public static final DERObjectIdentifier id_isismtt_at_requestedCertificate = new DERObjectIdentifier(id_isismtt_at + ".10");
+
+    /**
+     * Base ObjectIdentifier for naming authorities
+     */
+    public static final DERObjectIdentifier id_isismtt_at_namingAuthorities = new DERObjectIdentifier(id_isismtt_at + ".11");
+
+    /**
+     * SingleOCSPResponse extension: Date, when certificate has been published
+     * in the directory and status information has become available. Currently,
+     * accrediting authorities enforce that SigG-conforming OCSP servers include
+     * this extension in the responses.
+     * 
+     * <pre>
+     *      CertInDirSince ::= GeneralizedTime
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_certInDirSince = new DERObjectIdentifier(id_isismtt_at + ".12");
+
+    /**
+     * Hash of a certificate in OCSP.
+     * 
+     * @see org.bouncycastle.asn1.isismtt.ocsp.CertHash
+     */
+    public static final DERObjectIdentifier id_isismtt_at_certHash = new DERObjectIdentifier(id_isismtt_at + ".13");
+
+    /**
+     * <pre>
+     *          NameAtBirth ::= DirectoryString(SIZE(1..64)
+     * </pre>
+     * 
+     * Used in
+     * {@link org.bouncycastle.asn1.x509.SubjectDirectoryAttributes SubjectDirectoryAttributes}
+     */
+    public static final DERObjectIdentifier id_isismtt_at_nameAtBirth = new DERObjectIdentifier(id_isismtt_at + ".14");
+
+    /**
+     * Some other information of non-restrictive nature regarding the usage of
+     * this certificate. May be used as attribute in atribute certificate or as
+     * extension in a certificate.
+     * 
+     * <pre>
+     *               AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
+     * </pre>
+     * 
+     * @see org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
+     */
+    public static final DERObjectIdentifier id_isismtt_at_additionalInformation = new DERObjectIdentifier(id_isismtt_at + ".15");
+
+    /**
+     * Indicates that an attribute certificate exists, which limits the
+     * usability of this public key certificate. Whenever verifying a signature
+     * with the help of this certificate, the content of the corresponding
+     * attribute certificate should be concerned. This extension MUST be
+     * included in a PKC, if a corresponding attribute certificate (having the
+     * PKC as base certificate) contains some attribute that restricts the
+     * usability of the PKC too. Attribute certificates with restricting content
+     * MUST always be included in the signed document.
+     * 
+     * <pre>
+     *                   LiabilityLimitationFlagSyntax ::= BOOLEAN
+     * </pre>
+     */
+    public static final DERObjectIdentifier id_isismtt_at_liabilityLimitationFlag = new DERObjectIdentifier("0.2.262.1.10.12.0");
+}
diff --git a/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java b/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
deleted file mode 100644
index c384e8a..0000000
--- a/src/main/java/org/bouncycastle/asn1/misc/IDEACBCPar.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.bouncycastle.asn1.misc;
-
-import org.bouncycastle.asn1.*;
-
-public class IDEACBCPar
-    extends ASN1Encodable
-{
-    ASN1OctetString  iv;
-
-    public static IDEACBCPar getInstance(
-        Object  o)
-    {
-        if (o instanceof IDEACBCPar)
-        {
-            return (IDEACBCPar)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new IDEACBCPar((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in IDEACBCPar factory");
-    }
-
-    public IDEACBCPar(
-        byte[]  iv)
-    {
-        this.iv = new DEROctetString(iv);
-    }
-
-    public IDEACBCPar(
-        ASN1Sequence  seq)
-    {
-        if (seq.size() == 1)
-        {
-            iv = (ASN1OctetString)seq.getObjectAt(0);
-        }
-        else
-        {
-            iv = null;
-        }
-    }
-
-    public byte[] getIV()
-    {
-        if (iv != null)
-        {
-            return iv.getOctets();
-        }
-        else
-        {
-            return null;
-        }
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * IDEA-CBCPar ::= SEQUENCE {
-     *                      iv    OCTET STRING OPTIONAL -- exactly 8 octets
-     *                  }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        if (iv != null)
-        {
-            v.add(iv);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index b2d46e2..11a03d5 100644
--- a/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -27,4 +27,20 @@
     // CZAG - country, zip, age, and gender
     //
     static final DERObjectIdentifier    verisignCzagExtension   = new DERObjectIdentifier(verisign + ".6.3");
+    // D&B D-U-N-S number
+    static final DERObjectIdentifier    verisignDnbDunsNumber   = new DERObjectIdentifier(verisign + ".6.15");
+
+    //
+    // Novell
+    //       iso/itu(2) country(16) us(840) organization(1) novell(113719)
+    //
+    static final String                 novell                  = "2.16.840.1.113719";
+    static final DERObjectIdentifier    novellSecurityAttribs   = new DERObjectIdentifier(novell + ".1.9.4.1");
+
+    //
+    // Entrust
+    //       iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7)
+    //
+    static final String                 entrust                 = "1.2.840.113533.7";
+    static final DERObjectIdentifier    entrustVersionExtension = new DERObjectIdentifier(entrust + ".65.0");
 }
diff --git a/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java b/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java
deleted file mode 100644
index 57df8b0..0000000
--- a/src/main/java/org/bouncycastle/asn1/mozilla/PublicKeyAndChallenge.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.bouncycastle.asn1.mozilla;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-
-/**
- * This is designed to parse
- * the PublicKeyAndChallenge created by the KEYGEN tag included by
- * Mozilla based browsers.
- *  <pre>
- *  PublicKeyAndChallenge ::= SEQUENCE {
- *    spki SubjectPublicKeyInfo,
- *    challenge IA5STRING
- *  }
- *
- *  </pre>
- */
-public class PublicKeyAndChallenge
-    extends ASN1Encodable
-{
-    private ASN1Sequence         pkacSeq;
-    private SubjectPublicKeyInfo spki;
-    private DERIA5String         challenge;
-
-    public static PublicKeyAndChallenge getInstance(Object obj)
-    {
-        if (obj instanceof PublicKeyAndChallenge)
-        {
-            return (PublicKeyAndChallenge)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new PublicKeyAndChallenge((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unkown object in factory");
-    }
-
-    public PublicKeyAndChallenge(ASN1Sequence seq)
-    {
-        pkacSeq = seq;
-        spki = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(0));
-        challenge = DERIA5String.getInstance(seq.getObjectAt(1));
-    }
-
-    public DERObject toASN1Object()
-    {
-        return pkacSeq;
-    }
-
-    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
-    {
-        return spki;
-    }
-
-    public DERIA5String getChallenge()
-    {
-        return challenge;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
index e4a2777..a89d96d 100644
--- a/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -25,18 +25,24 @@
     static final DERObjectIdentifier    id_aes128_OFB           = new DERObjectIdentifier(aes + ".3"); 
     static final DERObjectIdentifier    id_aes128_CFB           = new DERObjectIdentifier(aes + ".4"); 
     static final DERObjectIdentifier    id_aes128_wrap          = new DERObjectIdentifier(aes + ".5");
+    static final DERObjectIdentifier    id_aes128_GCM           = new DERObjectIdentifier(aes + ".6");
+    static final DERObjectIdentifier    id_aes128_CCM           = new DERObjectIdentifier(aes + ".7");
     
     static final DERObjectIdentifier    id_aes192_ECB           = new DERObjectIdentifier(aes + ".21"); 
     static final DERObjectIdentifier    id_aes192_CBC           = new DERObjectIdentifier(aes + ".22"); 
     static final DERObjectIdentifier    id_aes192_OFB           = new DERObjectIdentifier(aes + ".23"); 
     static final DERObjectIdentifier    id_aes192_CFB           = new DERObjectIdentifier(aes + ".24"); 
     static final DERObjectIdentifier    id_aes192_wrap          = new DERObjectIdentifier(aes + ".25");
+    static final DERObjectIdentifier    id_aes192_GCM           = new DERObjectIdentifier(aes + ".26");
+    static final DERObjectIdentifier    id_aes192_CCM           = new DERObjectIdentifier(aes + ".27");
     
     static final DERObjectIdentifier    id_aes256_ECB           = new DERObjectIdentifier(aes + ".41"); 
     static final DERObjectIdentifier    id_aes256_CBC           = new DERObjectIdentifier(aes + ".42");
     static final DERObjectIdentifier    id_aes256_OFB           = new DERObjectIdentifier(aes + ".43"); 
     static final DERObjectIdentifier    id_aes256_CFB           = new DERObjectIdentifier(aes + ".44"); 
     static final DERObjectIdentifier    id_aes256_wrap          = new DERObjectIdentifier(aes + ".45"); 
+    static final DERObjectIdentifier    id_aes256_GCM           = new DERObjectIdentifier(aes + ".46");
+    static final DERObjectIdentifier    id_aes256_CCM           = new DERObjectIdentifier(aes + ".47");
 
     //
     // signatures
@@ -44,5 +50,7 @@
     static final DERObjectIdentifier    id_dsa_with_sha2        = new DERObjectIdentifier(nistAlgorithm + ".3"); 
 
     static final DERObjectIdentifier    dsa_with_sha224         = new DERObjectIdentifier(id_dsa_with_sha2 + ".1"); 
-    static final DERObjectIdentifier    dsa_with_sha256         = new DERObjectIdentifier(id_dsa_with_sha2 + ".2"); 
+    static final DERObjectIdentifier    dsa_with_sha256         = new DERObjectIdentifier(id_dsa_with_sha2 + ".2");
+    static final DERObjectIdentifier    dsa_with_sha384         = new DERObjectIdentifier(id_dsa_with_sha2 + ".3");
+    static final DERObjectIdentifier    dsa_with_sha512         = new DERObjectIdentifier(id_dsa_with_sha2 + ".4"); 
 }
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java b/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
deleted file mode 100644
index c78e193..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class BasicOCSPResponse
-    extends ASN1Encodable
-{
-    private ResponseData        tbsResponseData;
-    private AlgorithmIdentifier signatureAlgorithm;
-    private DERBitString        signature;
-    private ASN1Sequence        certs;
-
-    public BasicOCSPResponse(
-        ResponseData        tbsResponseData,
-        AlgorithmIdentifier signatureAlgorithm,
-        DERBitString        signature,
-        ASN1Sequence        certs)
-    {
-        this.tbsResponseData = tbsResponseData;
-        this.signatureAlgorithm = signatureAlgorithm;
-        this.signature = signature;
-        this.certs = certs;
-    }
-
-    public BasicOCSPResponse(
-        ASN1Sequence    seq)
-    {
-        this.tbsResponseData = ResponseData.getInstance(seq.getObjectAt(0));
-        this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-        this.signature = (DERBitString)seq.getObjectAt(2);
-
-        if (seq.size() > 3)
-        {
-            this.certs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(3), true);
-        }
-    }
-
-    public static BasicOCSPResponse getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static BasicOCSPResponse getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof BasicOCSPResponse)
-        {
-            return (BasicOCSPResponse)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new BasicOCSPResponse((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public ResponseData getTbsResponseData()
-    {
-        return tbsResponseData;
-    }
-
-    public AlgorithmIdentifier getSignatureAlgorithm()
-    {
-        return signatureAlgorithm;
-    }
-
-    public DERBitString getSignature()
-    {
-        return signature;
-    }
-
-    public ASN1Sequence getCerts()
-    {
-        return certs;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * BasicOCSPResponse       ::= SEQUENCE {
-     *      tbsResponseData      ResponseData,
-     *      signatureAlgorithm   AlgorithmIdentifier,
-     *      signature            BIT STRING,
-     *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(tbsResponseData);
-        v.add(signatureAlgorithm);
-        v.add(signature);
-        if (certs != null)
-        {
-            v.add(new DERTaggedObject(true, 0, certs));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java b/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java
deleted file mode 100644
index 2f5fe85..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/CertID.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class CertID
-    extends ASN1Encodable
-{
-    AlgorithmIdentifier    hashAlgorithm;
-    ASN1OctetString        issuerNameHash;
-    ASN1OctetString        issuerKeyHash;
-    DERInteger             serialNumber;
-
-    public CertID(
-        AlgorithmIdentifier hashAlgorithm,
-        ASN1OctetString     issuerNameHash,
-        ASN1OctetString     issuerKeyHash,
-        DERInteger          serialNumber)
-    {
-        this.hashAlgorithm = hashAlgorithm;
-        this.issuerNameHash = issuerNameHash;
-        this.issuerKeyHash = issuerKeyHash;
-        this.serialNumber = serialNumber;
-    }
-
-    public CertID(
-        ASN1Sequence    seq)
-    {
-        hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
-        issuerNameHash = (ASN1OctetString)seq.getObjectAt(1);
-        issuerKeyHash = (ASN1OctetString)seq.getObjectAt(2);
-        serialNumber = (DERInteger)seq.getObjectAt(3);
-    }
-
-    public static CertID getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static CertID getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof CertID)
-        {
-            return (CertID)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new CertID((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public AlgorithmIdentifier getHashAlgorithm()
-    {
-        return hashAlgorithm;
-    }
-
-    public ASN1OctetString getIssuerNameHash()
-    {
-        return issuerNameHash;
-    }
-
-    public ASN1OctetString getIssuerKeyHash()
-    {
-        return issuerKeyHash;
-    }
-
-    public DERInteger getSerialNumber()
-    {
-        return serialNumber;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * CertID          ::=     SEQUENCE {
-     *     hashAlgorithm       AlgorithmIdentifier,
-     *     issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
-     *     issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
-     *     serialNumber        CertificateSerialNumber }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(hashAlgorithm);
-        v.add(issuerNameHash);
-        v.add(issuerKeyHash);
-        v.add(serialNumber);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java b/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
deleted file mode 100644
index 1868bfc..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1Choice;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class CertStatus
-    extends ASN1Encodable
-    implements ASN1Choice
-{
-    private int             tagNo;
-    private DEREncodable    value;
-
-    /**
-     * create a CertStatus object with a tag of zero.
-     */
-    public CertStatus()
-    {
-        tagNo = 0;
-        // BEGIN android-changed
-        value = DERNull.THE_ONE;
-        // END android-changed
-    }
-
-    public CertStatus(
-        RevokedInfo info)
-    {
-        tagNo = 1;
-        value = info;
-    }
-
-    public CertStatus(
-        int tagNo,
-        DEREncodable    value)
-    {
-        this.tagNo = tagNo;
-        this.value = value;
-    }
-
-    public CertStatus(
-        ASN1TaggedObject    choice)
-    {
-        this.tagNo = choice.getTagNo();
-
-        switch (choice.getTagNo())
-        {
-        case 0:
-            // BEGIN android-changed
-            value = DERNull.THE_ONE;
-            // END android-changed
-            break;
-        case 1:
-            value = RevokedInfo.getInstance(choice, false);
-            break;
-        case 2:
-            // BEGIN android-changed
-            value = DERNull.THE_ONE;
-            // END android-changed
-        }
-    }
-
-    public static CertStatus getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof CertStatus)
-        {
-            return (CertStatus)obj;
-        }
-        else if (obj instanceof ASN1TaggedObject)
-        {
-            return new CertStatus((ASN1TaggedObject)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public static CertStatus getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(obj.getObject()); // must be explicitly tagged
-    }
-    
-    public int getTagNo()
-    {
-        return tagNo;
-    }
-
-    public DEREncodable getStatus()
-    {
-        return value;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     *  CertStatus ::= CHOICE {
-     *                  good        [0]     IMPLICIT NULL,
-     *                  revoked     [1]     IMPLICIT RevokedInfo,
-     *                  unknown     [2]     IMPLICIT UnknownInfo }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        return new DERTaggedObject(false, tagNo, value);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java b/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
deleted file mode 100644
index c933ac0..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.*;
-
-public class CrlID
-    extends ASN1Encodable
-{
-    DERIA5String        crlUrl;
-    DERInteger          crlNum;
-    DERGeneralizedTime  crlTime;
-
-    public CrlID(
-        ASN1Sequence    seq)
-    {
-        Enumeration    e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            ASN1TaggedObject    o = (ASN1TaggedObject)e.nextElement();
-
-            switch (o.getTagNo())
-            {
-            case 0:
-                crlUrl = DERIA5String.getInstance(o, true);
-                break;
-            case 1:
-                crlNum = DERInteger.getInstance(o, true);
-                break;
-            case 2:
-                crlTime = DERGeneralizedTime.getInstance(o, true);
-                break;
-            default:
-                throw new IllegalArgumentException(
-                        "unknown tag number: " + o.getTagNo());
-            }
-        }
-    }
-
-    public DERIA5String getCrlUrl()
-    {
-        return crlUrl;
-    }
-
-    public DERInteger getCrlNum()
-    {
-        return crlNum;
-    }
-
-    public DERGeneralizedTime getCrlTime()
-    {
-        return crlTime;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * CrlID ::= SEQUENCE {
-     *     crlUrl               [0]     EXPLICIT IA5String OPTIONAL,
-     *     crlNum               [1]     EXPLICIT INTEGER OPTIONAL,
-     *     crlTime              [2]     EXPLICIT GeneralizedTime OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        if (crlUrl != null)
-        {
-            v.add(new DERTaggedObject(true, 0, crlUrl));
-        }
-
-        if (crlNum != null)
-        {
-            v.add(new DERTaggedObject(true, 1, crlNum));
-        }
-
-        if (crlTime != null)
-        {
-            v.add(new DERTaggedObject(true, 2, crlTime));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
deleted file mode 100644
index f247270..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public interface OCSPObjectIdentifiers
-{
-    public static final String pkix_ocsp = "1.3.6.1.5.5.7.48.1";
-
-    public static final DERObjectIdentifier id_pkix_ocsp = new DERObjectIdentifier(pkix_ocsp);
-    public static final DERObjectIdentifier id_pkix_ocsp_basic = new DERObjectIdentifier(pkix_ocsp + ".1");
-    
-    //
-    // extensions
-    //
-    public static final DERObjectIdentifier id_pkix_ocsp_nonce = new DERObjectIdentifier(pkix_ocsp + ".2");
-    public static final DERObjectIdentifier id_pkix_ocsp_crl = new DERObjectIdentifier(pkix_ocsp + ".3");
-    
-    public static final DERObjectIdentifier id_pkix_ocsp_response = new DERObjectIdentifier(pkix_ocsp + ".4");
-    public static final DERObjectIdentifier id_pkix_ocsp_nocheck = new DERObjectIdentifier(pkix_ocsp + ".5");
-    public static final DERObjectIdentifier id_pkix_ocsp_archive_cutoff = new DERObjectIdentifier(pkix_ocsp + ".6");
-    public static final DERObjectIdentifier id_pkix_ocsp_service_locator = new DERObjectIdentifier(pkix_ocsp + ".7");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java b/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java
deleted file mode 100644
index 3f379de..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPRequest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class OCSPRequest
-    extends ASN1Encodable
-{
-    TBSRequest      tbsRequest;
-    Signature       optionalSignature;
-
-    public OCSPRequest(
-        TBSRequest  tbsRequest,
-        Signature   optionalSignature)
-    {
-        this.tbsRequest = tbsRequest;
-        this.optionalSignature = optionalSignature;
-    }
-
-    public OCSPRequest(
-        ASN1Sequence    seq)
-    {
-        tbsRequest = TBSRequest.getInstance(seq.getObjectAt(0));
-
-        if (seq.size() == 2)
-        {
-            optionalSignature = Signature.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true);
-        }
-    }
-    
-    public static OCSPRequest getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static OCSPRequest getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof OCSPRequest)
-        {
-            return (OCSPRequest)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new OCSPRequest((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-    
-    public TBSRequest getTbsRequest()
-    {
-        return tbsRequest;
-    }
-
-    public Signature getOptionalSignature()
-    {
-        return optionalSignature;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OCSPRequest     ::=     SEQUENCE {
-     *     tbsRequest                  TBSRequest,
-     *     optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(tbsRequest);
-
-        if (optionalSignature != null)
-        {
-            v.add(new DERTaggedObject(true, 0, optionalSignature));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java b/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java
deleted file mode 100644
index 9fbf740..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponse.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREnumerated;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-public class OCSPResponse
-    extends ASN1Encodable
-{
-    OCSPResponseStatus    responseStatus;
-    ResponseBytes        responseBytes;
-
-    public OCSPResponse(
-        OCSPResponseStatus  responseStatus,
-        ResponseBytes       responseBytes)
-    {
-        this.responseStatus = responseStatus;
-        this.responseBytes = responseBytes;
-    }
-
-    public OCSPResponse(
-        ASN1Sequence    seq)
-    {
-        responseStatus = new OCSPResponseStatus(
-                            DEREnumerated.getInstance(seq.getObjectAt(0)));
-
-        if (seq.size() == 2)
-        {
-            responseBytes = ResponseBytes.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true);
-        }
-    }
-
-    public static OCSPResponse getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static OCSPResponse getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof OCSPResponse)
-        {
-            return (OCSPResponse)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new OCSPResponse((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public OCSPResponseStatus getResponseStatus()
-    {
-        return responseStatus;
-    }
-
-    public ResponseBytes getResponseBytes()
-    {
-        return responseBytes;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OCSPResponse ::= SEQUENCE {
-     *     responseStatus         OCSPResponseStatus,
-     *     responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(responseStatus);
-
-        if (responseBytes != null)
-        {
-            v.add(new DERTaggedObject(true, 0, responseBytes));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java b/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
deleted file mode 100644
index 7185235..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.DEREnumerated;
-
-public class OCSPResponseStatus
-    extends DEREnumerated
-{
-    public static final int SUCCESSFUL = 0;
-    public static final int MALFORMED_REQUEST = 1;
-    public static final int INTERNAL_ERROR = 2;
-    public static final int TRY_LATER = 3;
-    public static final int SIG_REQUIRED = 5;
-    public static final int UNAUTHORIZED = 6;
-
-    /**
-     * The OCSPResponseStatus enumeration.
-     * <pre>
-     * OCSPResponseStatus ::= ENUMERATED {
-     *     successful            (0),  --Response has valid confirmations
-     *     malformedRequest      (1),  --Illegal confirmation request
-     *     internalError         (2),  --Internal error in issuer
-     *     tryLater              (3),  --Try again later
-     *                                 --(4) is not used
-     *     sigRequired           (5),  --Must sign the request
-     *     unauthorized          (6)   --Request unauthorized
-     * }
-     * </pre>
-     */
-    public OCSPResponseStatus(
-        int value)
-    {
-        super(value);
-    }
-
-    public OCSPResponseStatus(
-        DEREnumerated value)
-    {
-        super(value.getValue().intValue());
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/Request.java b/src/main/java/org/bouncycastle/asn1/ocsp/Request.java
deleted file mode 100644
index 350fa17..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/Request.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class Request
-    extends ASN1Encodable
-{
-    CertID            reqCert;
-    X509Extensions    singleRequestExtensions;
-
-    public Request(
-        CertID          reqCert,
-        X509Extensions  singleRequestExtensions)
-    {
-        this.reqCert = reqCert;
-        this.singleRequestExtensions = singleRequestExtensions;
-    }
-
-    public Request(
-        ASN1Sequence    seq)
-    {
-        reqCert = CertID.getInstance(seq.getObjectAt(0));
-
-        if (seq.size() == 2)
-        {
-            singleRequestExtensions = X509Extensions.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true);
-        }
-    }
-
-    public static Request getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static Request getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof Request)
-        {
-            return (Request)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new Request((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public CertID getReqCert()
-    {
-        return reqCert;
-    }
-
-    public X509Extensions getSingleRequestExtensions()
-    {
-        return singleRequestExtensions;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Request         ::=     SEQUENCE {
-     *     reqCert                     CertID,
-     *     singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(reqCert);
-
-        if (singleRequestExtensions != null)
-        {
-            v.add(new DERTaggedObject(true, 0, singleRequestExtensions));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java b/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
deleted file mode 100644
index 09cdf11..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1Choice;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.X509Name;
-
-public class ResponderID
-    extends ASN1Encodable
-    implements ASN1Choice
-{
-    private DEREncodable    value;
-
-    public ResponderID(
-        ASN1OctetString    value)
-    {
-        this.value = value;
-    }
-
-    public ResponderID(
-        X509Name    value)
-    {
-        this.value = value;
-    }
-
-    public static ResponderID getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof ResponderID)
-        {
-            return (ResponderID)obj;
-        }
-        else if (obj instanceof DEROctetString)
-        {
-            return new ResponderID((DEROctetString)obj);
-        }
-        else if (obj instanceof ASN1TaggedObject)
-        {
-            ASN1TaggedObject    o = (ASN1TaggedObject)obj;
-
-            if (o.getTagNo() == 1)
-            {
-                return new ResponderID(X509Name.getInstance(o, true));
-            }
-            else
-            {
-                return new ResponderID(ASN1OctetString.getInstance(o, true));
-            }
-        }
-
-        return new ResponderID(X509Name.getInstance(obj));
-    }
-
-    public static ResponderID getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(obj.getObject()); // must be explicitly tagged
-    }
-    
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * ResponderID ::= CHOICE {
-     *      byName          [1] Name,
-     *      byKey           [2] KeyHash }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        if (value instanceof ASN1OctetString)
-        {
-            return new DERTaggedObject(true, 2, value);
-        }
-
-        return new DERTaggedObject(true, 1, value);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java b/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
deleted file mode 100644
index 997f41f..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class ResponseBytes
-    extends ASN1Encodable
-{
-    DERObjectIdentifier    responseType;
-    ASN1OctetString        response;
-
-    public ResponseBytes(
-        DERObjectIdentifier responseType,
-        ASN1OctetString     response)
-    {
-        this.responseType = responseType;
-        this.response = response;
-    }
-
-    public ResponseBytes(
-        ASN1Sequence    seq)
-    {
-        responseType = (DERObjectIdentifier)seq.getObjectAt(0);
-        response = (ASN1OctetString)seq.getObjectAt(1);
-    }
-
-    public static ResponseBytes getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static ResponseBytes getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof ResponseBytes)
-        {
-            return (ResponseBytes)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new ResponseBytes((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public DERObjectIdentifier getResponseType()
-    {
-        return responseType;
-    }
-
-    public ASN1OctetString getResponse()
-    {
-        return response;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * ResponseBytes ::=       SEQUENCE {
-     *     responseType   OBJECT IDENTIFIER,
-     *     response       OCTET STRING }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(responseType);
-        v.add(response);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java b/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java
deleted file mode 100644
index db0e586..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/ResponseData.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class ResponseData
-    extends ASN1Encodable
-{
-    private static final DERInteger V1 = new DERInteger(0);
-    
-    private boolean             versionPresent;
-    
-    private DERInteger          version;
-    private ResponderID         responderID;
-    private DERGeneralizedTime  producedAt;
-    private ASN1Sequence        responses;
-    private X509Extensions      responseExtensions;
-
-    public ResponseData(
-        DERInteger          version,
-        ResponderID         responderID,
-        DERGeneralizedTime  producedAt,
-        ASN1Sequence        responses,
-        X509Extensions      responseExtensions)
-    {
-        this.version = version;
-        this.responderID = responderID;
-        this.producedAt = producedAt;
-        this.responses = responses;
-        this.responseExtensions = responseExtensions;
-    }
-    
-    public ResponseData(
-        ResponderID         responderID,
-        DERGeneralizedTime  producedAt,
-        ASN1Sequence        responses,
-        X509Extensions      responseExtensions)
-    {
-        this(V1, responderID, producedAt, responses, responseExtensions);
-    }
-    
-    public ResponseData(
-        ASN1Sequence    seq)
-    {
-        int index = 0;
-
-        if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
-        {
-            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(0);
-
-            if (o.getTagNo() == 0)
-            {
-                this.versionPresent = true;
-                this.version = DERInteger.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(0), true);
-                index++;
-            }
-            else
-            {
-                this.version = V1;
-            }
-        }
-        else
-        {
-            this.version = V1;
-        }
-
-        this.responderID = ResponderID.getInstance(seq.getObjectAt(index++));
-        this.producedAt = (DERGeneralizedTime)seq.getObjectAt(index++);
-        this.responses = (ASN1Sequence)seq.getObjectAt(index++);
-
-        if (seq.size() > index)
-        {
-            this.responseExtensions = X509Extensions.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(index), true);
-        }
-    }
-
-    public static ResponseData getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static ResponseData getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof ResponseData)
-        {
-            return (ResponseData)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new ResponseData((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public ResponderID getResponderID()
-    {
-        return responderID;
-    }
-
-    public DERGeneralizedTime getProducedAt()
-    {
-        return producedAt;
-    }
-
-    public ASN1Sequence getResponses()
-    {
-        return responses;
-    }
-
-    public X509Extensions getResponseExtensions()
-    {
-        return responseExtensions;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * ResponseData ::= SEQUENCE {
-     *     version              [0] EXPLICIT Version DEFAULT v1,
-     *     responderID              ResponderID,
-     *     producedAt               GeneralizedTime,
-     *     responses                SEQUENCE OF SingleResponse,
-     *     responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        if (versionPresent || !version.equals(V1))
-        {
-            v.add(new DERTaggedObject(true, 0, version));
-        }
-
-        v.add(responderID);
-        v.add(producedAt);
-        v.add(responses);
-        if (responseExtensions != null)
-        {
-            v.add(new DERTaggedObject(true, 1, responseExtensions));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
deleted file mode 100644
index 501a117..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREnumerated;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.CRLReason;
-
-public class RevokedInfo
-    extends ASN1Encodable
-{
-    private DERGeneralizedTime  revocationTime;
-    private CRLReason           revocationReason;
-
-    public RevokedInfo(
-        DERGeneralizedTime  revocationTime,
-        CRLReason           revocationReason)
-    {
-        this.revocationTime = revocationTime;
-        this.revocationReason = revocationReason;
-    }
-
-    public RevokedInfo(
-        ASN1Sequence    seq)
-    {
-        this.revocationTime = (DERGeneralizedTime)seq.getObjectAt(0);
-
-        if (seq.size() > 1)
-        {
-            this.revocationReason = new CRLReason(DEREnumerated.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true));
-        }
-    }
-
-    public static RevokedInfo getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static RevokedInfo getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof RevokedInfo)
-        {
-            return (RevokedInfo)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new RevokedInfo((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public DERGeneralizedTime getRevocationTime()
-    {
-        return revocationTime;
-    }
-
-    public CRLReason getRevocationReason()
-    {
-        return revocationReason;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RevokedInfo ::= SEQUENCE {
-     *      revocationTime              GeneralizedTime,
-     *      revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(revocationTime);
-        if (revocationReason != null)
-        {
-            v.add(new DERTaggedObject(true, 0, revocationReason));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java b/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java
deleted file mode 100644
index 296dd53..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/ServiceLocator.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.X509Name;
-
-public class ServiceLocator
-    extends ASN1Encodable
-{
-    X509Name    issuer;
-    DERObject    locator;
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * ServiceLocator ::= SEQUENCE {
-     *     issuer    Name,
-     *     locator   AuthorityInfoAccessSyntax OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(issuer);
-
-        if (locator != null)
-        {
-            v.add(locator);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java b/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java
deleted file mode 100644
index 907fd9b..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/Signature.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class Signature
-    extends ASN1Encodable
-{
-    AlgorithmIdentifier signatureAlgorithm;
-    DERBitString        signature;
-    ASN1Sequence        certs;
-
-    public Signature(
-        AlgorithmIdentifier signatureAlgorithm,
-        DERBitString        signature)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-        this.signature = signature;
-    }
-
-    public Signature(
-        AlgorithmIdentifier signatureAlgorithm,
-        DERBitString        signature,
-        ASN1Sequence        certs)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-        this.signature = signature;
-        this.certs = certs;
-    }
-
-    public Signature(
-        ASN1Sequence    seq)
-    {
-        signatureAlgorithm  = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
-        signature = (DERBitString)seq.getObjectAt(1);
-
-        if (seq.size() == 3)
-        {
-            certs = ASN1Sequence.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(2), true);
-        }
-    }
-
-    public static Signature getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static Signature getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof Signature)
-        {
-            return (Signature)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new Signature((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public AlgorithmIdentifier getSignatureAlgorithm()
-    {
-        return signatureAlgorithm;
-    }
-
-    public DERBitString getSignature()
-    {
-        return signature;
-    }
-
-    public ASN1Sequence getCerts()
-    {
-        return certs;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Signature       ::=     SEQUENCE {
-     *     signatureAlgorithm      AlgorithmIdentifier,
-     *     signature               BIT STRING,
-     *     certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        v.add(signatureAlgorithm);
-        v.add(signature);
-
-        if (certs != null)
-        {
-            v.add(new DERTaggedObject(true, 0, certs));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java b/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java
deleted file mode 100644
index a16f14b..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/SingleResponse.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class SingleResponse
-    extends ASN1Encodable
-{
-    private CertID              certID;
-    private CertStatus          certStatus;
-    private DERGeneralizedTime  thisUpdate;
-    private DERGeneralizedTime  nextUpdate;
-    private X509Extensions      singleExtensions;
-
-    public SingleResponse(
-        CertID              certID,
-        CertStatus          certStatus,
-        DERGeneralizedTime  thisUpdate,
-        DERGeneralizedTime  nextUpdate,
-        X509Extensions      singleExtensions)
-    {
-        this.certID = certID;
-        this.certStatus = certStatus;
-        this.thisUpdate = thisUpdate;
-        this.nextUpdate = nextUpdate;
-        this.singleExtensions = singleExtensions;
-    }
-
-    public SingleResponse(
-        ASN1Sequence    seq)
-    {
-        this.certID = CertID.getInstance(seq.getObjectAt(0));
-        this.certStatus = CertStatus.getInstance(seq.getObjectAt(1));
-        this.thisUpdate = (DERGeneralizedTime)seq.getObjectAt(2);
-
-        if (seq.size() > 4)
-        {
-            this.nextUpdate = DERGeneralizedTime.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(3), true);
-            this.singleExtensions = X509Extensions.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(4), true);
-        }
-        else if (seq.size() > 3)
-        {
-            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(3);
-
-            if (o.getTagNo() == 0)
-            {
-                this.nextUpdate = DERGeneralizedTime.getInstance(o, true);
-            }
-            else
-            {
-                this.singleExtensions = X509Extensions.getInstance(o, true);
-            }
-        }
-    }
-
-    public static SingleResponse getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static SingleResponse getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof SingleResponse)
-        {
-            return (SingleResponse)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new SingleResponse((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public CertID getCertID()
-    {
-        return certID;
-    }
-
-    public CertStatus getCertStatus()
-    {
-        return certStatus;
-    }
-
-    public DERGeneralizedTime getThisUpdate()
-    {
-        return thisUpdate;
-    }
-
-    public DERGeneralizedTime getNextUpdate()
-    {
-        return nextUpdate;
-    }
-
-    public X509Extensions getSingleExtensions()
-    {
-        return singleExtensions;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     *  SingleResponse ::= SEQUENCE {
-     *          certID                       CertID,
-     *          certStatus                   CertStatus,
-     *          thisUpdate                   GeneralizedTime,
-     *          nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
-     *          singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(certID);
-        v.add(certStatus);
-        v.add(thisUpdate);
-
-        if (nextUpdate != null)
-        {
-            v.add(new DERTaggedObject(true, 0, nextUpdate));
-        }
-
-        if (singleExtensions != null)
-        {
-            v.add(new DERTaggedObject(true, 1, singleExtensions));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java b/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java
deleted file mode 100644
index fd4e86b..0000000
--- a/src/main/java/org/bouncycastle/asn1/ocsp/TBSRequest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package org.bouncycastle.asn1.ocsp;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class TBSRequest
-    extends ASN1Encodable
-{
-    private static final DERInteger V1 = new DERInteger(0);
-    
-    DERInteger      version;
-    GeneralName     requestorName;
-    ASN1Sequence    requestList;
-    X509Extensions  requestExtensions;
-
-    public TBSRequest(
-        GeneralName     requestorName,
-        ASN1Sequence    requestList,
-        X509Extensions  requestExtensions)
-    {
-        this.version = V1;
-        this.requestorName = requestorName;
-        this.requestList = requestList;
-        this.requestExtensions = requestExtensions;
-    }
-
-    public TBSRequest(
-        ASN1Sequence    seq)
-    {
-        int    index = 0;
-
-        if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
-        {
-            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(0);
-
-            if (o.getTagNo() == 0)
-            {
-                version = DERInteger.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
-                index++;
-            }
-            else
-            {
-                version = V1;
-            }
-        }
-        else
-        {
-            version = V1;
-        }
-
-        if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
-        {
-            requestorName = GeneralName.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true);
-        }
-        
-        requestList = (ASN1Sequence)seq.getObjectAt(index++);
-
-        if (seq.size() == (index + 1))
-        {
-            requestExtensions = X509Extensions.getInstance((ASN1TaggedObject)seq.getObjectAt(index), true);
-        }
-    }
-
-    public static TBSRequest getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static TBSRequest getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof TBSRequest)
-        {
-            return (TBSRequest)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new TBSRequest((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public GeneralName getRequestorName()
-    {
-        return requestorName;
-    }
-
-    public ASN1Sequence getRequestList()
-    {
-        return requestList;
-    }
-
-    public X509Extensions getRequestExtensions()
-    {
-        return requestExtensions;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * TBSRequest      ::=     SEQUENCE {
-     *     version             [0]     EXPLICIT Version DEFAULT v1,
-     *     requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
-     *     requestList                 SEQUENCE OF Request,
-     *     requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector    v = new ASN1EncodableVector();
-
-        //
-        // if default don't include.
-        //
-        if (!version.equals(V1))
-        {
-            v.add(new DERTaggedObject(true, 0, version));
-        }
-        
-        if (requestorName != null)
-        {
-            v.add(new DERTaggedObject(true, 1, requestorName));
-        }
-
-        v.add(requestList);
-
-        if (requestExtensions != null)
-        {
-            v.add(new DERTaggedObject(true, 2, requestExtensions));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
index 4174e4d..d9690ec 100644
--- a/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
@@ -10,7 +10,12 @@
     static final DERObjectIdentifier    md5WithRSA              = new DERObjectIdentifier("1.3.14.3.2.3");
     static final DERObjectIdentifier    md4WithRSAEncryption    = new DERObjectIdentifier("1.3.14.3.2.4");
     
+    static final DERObjectIdentifier    desECB                  = new DERObjectIdentifier("1.3.14.3.2.6");
     static final DERObjectIdentifier    desCBC                  = new DERObjectIdentifier("1.3.14.3.2.7");
+    static final DERObjectIdentifier    desOFB                  = new DERObjectIdentifier("1.3.14.3.2.8");
+    static final DERObjectIdentifier    desCFB                  = new DERObjectIdentifier("1.3.14.3.2.9");
+
+    static final DERObjectIdentifier    desEDE                  = new DERObjectIdentifier("1.3.14.3.2.17");
     
     static final DERObjectIdentifier    idSHA1                  = new DERObjectIdentifier("1.3.14.3.2.26");
 
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java b/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
deleted file mode 100644
index 56d6870..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class Attribute
-    extends ASN1Encodable
-{
-    private DERObjectIdentifier attrType;
-    private ASN1Set             attrValues;
-
-    /**
-     * return an Attribute object from the given object.
-     *
-     * @param o the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     */
-    public static Attribute getInstance(
-        Object o)
-    {
-        if (o == null || o instanceof Attribute)
-        {
-            return (Attribute)o;
-        }
-        
-        if (o instanceof ASN1Sequence)
-        {
-            return new Attribute((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-    
-    public Attribute(
-        ASN1Sequence seq)
-    {
-        attrType = (DERObjectIdentifier)seq.getObjectAt(0);
-        attrValues = (ASN1Set)seq.getObjectAt(1);
-    }
-
-    public Attribute(
-        DERObjectIdentifier attrType,
-        ASN1Set             attrValues)
-    {
-        this.attrType = attrType;
-        this.attrValues = attrValues;
-    }
-
-    public DERObjectIdentifier getAttrType()
-    {
-        return attrType;
-    }
-    
-    public ASN1Set getAttrValues()
-    {
-        return attrValues;
-    }
-
-    /** 
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Attribute ::= SEQUENCE {
-     *     attrType OBJECT IDENTIFIER,
-     *     attrValues SET OF AttributeValue
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(attrType);
-        v.add(attrValues);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
index ba28f54..23772ce 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -25,6 +25,21 @@
     protected AlgorithmIdentifier sigAlgId = null;
     protected DERBitString sigBits = null;
 
+    public static CertificationRequest getInstance(Object o)
+    {
+        if (o instanceof CertificationRequest)
+        {
+            return (CertificationRequest)o;
+        }
+
+        if (o instanceof ASN1Sequence)
+        {
+            return new CertificationRequest((ASN1Sequence)o);
+        }
+
+        throw new IllegalArgumentException("Invalid object: " + o.getClass().getName());
+    }
+
     protected CertificationRequest()
     {
     }
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
index 3b9314d..4b737ea 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -49,7 +49,7 @@
             return new CertificationRequestInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public CertificationRequestInfo(
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java b/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
index 49b3a70..6b56c1a 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
@@ -31,7 +31,7 @@
             return new ContentInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public ContentInfo(
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java b/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
index 37d1303..75b81b4 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -40,7 +40,7 @@
              return new EncryptedData((ASN1Sequence)obj);
          }
 
-         throw new IllegalArgumentException("unknown object in factory");
+         throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
      
     public EncryptedData(
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
index b46c13f..77c4b04 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
@@ -46,7 +46,7 @@
             return new EncryptedPrivateKeyInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public AlgorithmIdentifier getEncryptionAlgorithm()
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java b/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
index 088d213..699e467 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
@@ -28,7 +28,7 @@
             return new IssuerAndSerialNumber((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public IssuerAndSerialNumber(
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java b/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
index 8f58c82..e85cf4a 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
@@ -15,6 +15,8 @@
 public class MacData
     extends ASN1Encodable
 {
+    private static final BigInteger ONE = BigInteger.valueOf(1);
+
     DigestInfo                  digInfo;
     byte[]                      salt;
     BigInteger                  iterationCount;
@@ -31,7 +33,7 @@
             return new MacData((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public MacData(
@@ -47,7 +49,7 @@
         }
         else
         {
-            this.iterationCount = BigInteger.valueOf(1);
+            this.iterationCount = ONE;
         }
     }
 
@@ -76,13 +78,28 @@
         return iterationCount;
     }
 
+    /**
+     * <pre>
+     * MacData ::= SEQUENCE {
+     *     mac      DigestInfo,
+     *     macSalt  OCTET STRING,
+     *     iterations INTEGER DEFAULT 1
+     *     -- Note: The default is for historic reasons and its use is deprecated. A
+     *     -- higher value, like 1024 is recommended.
+     * </pre>
+     * @return the basic DERObject construction.
+     */
     public DERObject toASN1Object()
     {
         ASN1EncodableVector  v = new ASN1EncodableVector();
 
         v.add(digInfo);
         v.add(new DEROctetString(salt));
-        v.add(new DERInteger(iterationCount));
+        
+        if (!iterationCount.equals(ONE))
+        {
+            v.add(new DERInteger(iterationCount));
+        }
 
         return new DERSequence(v);
     }
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java b/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
deleted file mode 100644
index 2817903..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-/**
- * @deprecated - use AlgorithmIdentifier and PBES2Parameters
- */
-public class PBES2Algorithms
-    extends AlgorithmIdentifier implements PKCSObjectIdentifiers
-{
-    private DERObjectIdentifier objectId;
-    private KeyDerivationFunc   func;
-    private EncryptionScheme    scheme;
-
-    public PBES2Algorithms(
-        ASN1Sequence  obj)
-    {
-        super(obj);
-
-        Enumeration     e = obj.getObjects();
-
-        objectId = (DERObjectIdentifier)e.nextElement();
-
-        ASN1Sequence seq = (ASN1Sequence)e.nextElement();
-
-        e = seq.getObjects();
-
-        ASN1Sequence  funcSeq = (ASN1Sequence)e.nextElement();
-
-        if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
-        {
-            func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
-        }
-        else
-        {
-            func = new KeyDerivationFunc(funcSeq);
-        }
-
-        scheme = new EncryptionScheme((ASN1Sequence)e.nextElement());
-    }
-
-    public DERObjectIdentifier getObjectId()
-    {
-        return objectId;
-    }
-
-    public KeyDerivationFunc getKeyDerivationFunc()
-    {
-        return func;
-    }
-
-    public EncryptionScheme getEncryptionScheme()
-    {
-        return scheme;
-    }
-
-    public DERObject getDERObject()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-        ASN1EncodableVector  subV = new ASN1EncodableVector();
-
-        v.add(objectId);
-
-        subV.add(func);
-        subV.add(scheme);
-        v.add(new DERSequence(subV));
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java b/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
deleted file mode 100644
index 57c773c..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-public class PBES2Parameters
-    extends ASN1Encodable
-    implements PKCSObjectIdentifiers
-{
-    private KeyDerivationFunc   func;
-    private EncryptionScheme    scheme;
-
-    public PBES2Parameters(
-        ASN1Sequence  obj)
-    {
-        Enumeration e = obj.getObjects();
-        ASN1Sequence  funcSeq = (ASN1Sequence)e.nextElement();
-
-        if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
-        {
-            func = new KeyDerivationFunc(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
-        }
-        else
-        {
-            func = new KeyDerivationFunc(funcSeq);
-        }
-
-        scheme = new EncryptionScheme((ASN1Sequence)e.nextElement());
-    }
-
-    public KeyDerivationFunc getKeyDerivationFunc()
-    {
-        return func;
-    }
-
-    public EncryptionScheme getEncryptionScheme()
-    {
-        return scheme;
-    }
-
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(func);
-        v.add(scheme);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
deleted file mode 100644
index 311766e..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import java.math.BigInteger;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-
-public class PBKDF2Params
-    extends ASN1Encodable
-{
-    ASN1OctetString     octStr;
-    DERInteger          iterationCount;
-    DERInteger          keyLength;
-
-    public static PBKDF2Params getInstance(
-        Object  obj)
-    {
-        if (obj instanceof PBKDF2Params)
-        {
-            return (PBKDF2Params)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new PBKDF2Params((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-    
-    public PBKDF2Params(
-        byte[]  salt,
-        int     iterationCount)
-    {
-        this.octStr = new DEROctetString(salt);
-        this.iterationCount = new DERInteger(iterationCount);
-    }
-    
-    public PBKDF2Params(
-        ASN1Sequence  seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        octStr = (ASN1OctetString)e.nextElement();
-        iterationCount = (DERInteger)e.nextElement();
-
-        if (e.hasMoreElements())
-        {
-            keyLength = (DERInteger)e.nextElement();
-        }
-        else
-        {
-            keyLength = null;
-        }
-    }
-
-    public byte[] getSalt()
-    {
-        return octStr.getOctets();
-    }
-
-    public BigInteger getIterationCount()
-    {
-        return iterationCount.getValue();
-    }
-
-    public BigInteger getKeyLength()
-    {
-        if (keyLength != null)
-        {
-            return keyLength.getValue();
-        }
-
-        return null;
-    }
-
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(octStr);
-        v.add(iterationCount);
-
-        if (keyLength != null)
-        {
-            v.add(keyLength);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java b/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
index 5dc3a5d..8817b35 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
@@ -44,7 +44,7 @@
             return new PKCS12PBEParams((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public BigInteger getIterations()
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index 1a7a553..3fec780 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -49,7 +49,9 @@
 
     static final DERObjectIdentifier    id_PBES2                = new DERObjectIdentifier(pkcs_5 + ".13");
 
-    static final DERObjectIdentifier    id_PBKDF2               = new DERObjectIdentifier(pkcs_5 + ".12");
+    // BEGIN android-removed
+    // static final DERObjectIdentifier    id_PBKDF2               = new DERObjectIdentifier(pkcs_5 + ".12");
+    // END android-removed
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
@@ -126,8 +128,16 @@
     static final DERObjectIdentifier    pkcs_9_at_friendlyName  = new DERObjectIdentifier(pkcs_9 + ".20");
     static final DERObjectIdentifier    pkcs_9_at_localKeyId    = new DERObjectIdentifier(pkcs_9 + ".21");
 
+    /** @deprecated use x509Certificate instead */
     static final DERObjectIdentifier    x509certType            = new DERObjectIdentifier(pkcs_9 + ".22.1");
 
+    static final String                 certTypes               = pkcs_9 + ".22";
+    static final DERObjectIdentifier    x509Certificate         = new DERObjectIdentifier(certTypes + ".1");
+    static final DERObjectIdentifier    sdsiCertificate         = new DERObjectIdentifier(certTypes + ".2");
+
+    static final String                 crlTypes                = pkcs_9 + ".23";
+    static final DERObjectIdentifier    x509Crl                 = new DERObjectIdentifier(crlTypes + ".1");
+
     static final DERObjectIdentifier    id_alg_PWRI_KEK    = new DERObjectIdentifier(pkcs_9 + ".16.3.9");
 
     //
@@ -138,19 +148,16 @@
     static final DERObjectIdentifier    sMIMECapabilitiesVersions = new DERObjectIdentifier(pkcs_9 + ".15.3");
 
     //
-    // other SMIME attributes
-    //
-    static final DERObjectIdentifier    id_aa_receiptRequest    = new DERObjectIdentifier(pkcs_9 + ".16.2.1");
-
-    //
     // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)}
     //
     static String id_ct = "1.2.840.113549.1.9.16.1";
-    
+
+    static final DERObjectIdentifier    id_ct_authData          = new DERObjectIdentifier(id_ct + ".2");
     static final DERObjectIdentifier    id_ct_TSTInfo           = new DERObjectIdentifier(id_ct + ".4");
     static final DERObjectIdentifier    id_ct_compressedData    = new DERObjectIdentifier(id_ct + ".9");
-    
+    static final DERObjectIdentifier    id_ct_authEnvelopedData = new DERObjectIdentifier(id_ct + ".23");
+
     //
     // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)}
@@ -169,20 +176,57 @@
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)}
     //
     static String id_aa = "1.2.840.113549.1.9.16.2";
+
+    static final DERObjectIdentifier id_aa_receiptRequest = new DERObjectIdentifier(id_aa + ".1");
     
+    static final DERObjectIdentifier id_aa_contentHint = new DERObjectIdentifier(id_aa + ".4"); // See RFC 2634
     /*
      * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11}
      * 
      */
-    static DERObjectIdentifier id_aa_encrypKeyPref = new DERObjectIdentifier(id_aa + ".11");
-    static DERObjectIdentifier id_aa_signingCertificate = new DERObjectIdentifier(id_aa + ".12");
+    static final DERObjectIdentifier id_aa_encrypKeyPref = new DERObjectIdentifier(id_aa + ".11");
+    static final DERObjectIdentifier id_aa_signingCertificate = new DERObjectIdentifier(id_aa + ".12");
+    static final DERObjectIdentifier id_aa_signingCertificateV2 = new DERObjectIdentifier(id_aa + ".47");
 
     static final DERObjectIdentifier id_aa_contentIdentifier = new DERObjectIdentifier(id_aa + ".7"); // See RFC 2634
-    static final DERObjectIdentifier id_aa_signatureTimeStampToken = new DERObjectIdentifier(id_aa + ".14"); // See RFC 3126
-    static final DERObjectIdentifier id_aa_sigPolicyId = new DERObjectIdentifier(id_aa + ".15"); // See RFC 3126
-    static final DERObjectIdentifier id_aa_commitmentType = new DERObjectIdentifier(id_aa + ".16"); // See RFC 3126
-    static final DERObjectIdentifier id_aa_signerLocation = new DERObjectIdentifier(id_aa + ".17"); // See RFC 3126
-    static final DERObjectIdentifier id_aa_otherSigCert = new DERObjectIdentifier(id_aa + ".19"); // See RFC 3126
+
+    /*
+     * RFC 3126
+     */
+    static final DERObjectIdentifier id_aa_signatureTimeStampToken = new DERObjectIdentifier(id_aa + ".14");
+    
+    static final DERObjectIdentifier id_aa_ets_sigPolicyId = new DERObjectIdentifier(id_aa + ".15");
+    static final DERObjectIdentifier id_aa_ets_commitmentType = new DERObjectIdentifier(id_aa + ".16");
+    static final DERObjectIdentifier id_aa_ets_signerLocation = new DERObjectIdentifier(id_aa + ".17");
+    static final DERObjectIdentifier id_aa_ets_signerAttr = new DERObjectIdentifier(id_aa + ".18");
+    static final DERObjectIdentifier id_aa_ets_otherSigCert = new DERObjectIdentifier(id_aa + ".19");
+    static final DERObjectIdentifier id_aa_ets_contentTimestamp = new DERObjectIdentifier(id_aa + ".20");
+    static final DERObjectIdentifier id_aa_ets_certificateRefs = new DERObjectIdentifier(id_aa + ".21");
+    static final DERObjectIdentifier id_aa_ets_revocationRefs = new DERObjectIdentifier(id_aa + ".22");
+    static final DERObjectIdentifier id_aa_ets_certValues = new DERObjectIdentifier(id_aa + ".23");
+    static final DERObjectIdentifier id_aa_ets_revocationValues = new DERObjectIdentifier(id_aa + ".24");
+    static final DERObjectIdentifier id_aa_ets_escTimeStamp = new DERObjectIdentifier(id_aa + ".25");
+    static final DERObjectIdentifier id_aa_ets_certCRLTimestamp = new DERObjectIdentifier(id_aa + ".26");
+    static final DERObjectIdentifier id_aa_ets_archiveTimestamp = new DERObjectIdentifier(id_aa + ".27");
+
+    /** @deprecated use id_aa_ets_sigPolicyId instead */
+    static final DERObjectIdentifier id_aa_sigPolicyId = id_aa_ets_sigPolicyId;
+    /** @deprecated use id_aa_ets_commitmentType instead */
+    static final DERObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType;
+    /** @deprecated use id_aa_ets_signerLocation instead */
+    static final DERObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation;
+    /** @deprecated use id_aa_ets_otherSigCert instead */
+    static final DERObjectIdentifier id_aa_otherSigCert = id_aa_ets_otherSigCert;
+    
+    //
+    // id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+    // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)}
+    //
+    final String id_spq = "1.2.840.113549.1.9.16.5";
+
+    static final DERObjectIdentifier id_spq_ets_uri = new DERObjectIdentifier(id_spq + ".1");
+    static final DERObjectIdentifier id_spq_ets_unotice = new DERObjectIdentifier(id_spq + ".2");
+
     //
     // pkcs-12 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
@@ -206,5 +250,7 @@
     static final DERObjectIdentifier    pbeWithSHAAnd128BitRC2_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".5");
     static final DERObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".6");
 
+    static final DERObjectIdentifier    id_alg_CMS3DESwrap = new DERObjectIdentifier("1.2.840.113549.1.9.16.3.6");
+    static final DERObjectIdentifier    id_alg_CMSRC2wrap = new DERObjectIdentifier("1.2.840.113549.1.9.16.3.7");
 }
 
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
index 5c384d8..a7fff2f 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -1,9 +1,5 @@
 package org.bouncycastle.asn1.pkcs;
 
-import java.io.IOException;
-import java.math.BigInteger;
-import java.util.Enumeration;
-
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1InputStream;
@@ -18,6 +14,10 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
 public class PrivateKeyInfo
     extends ASN1Encodable
 {
@@ -44,15 +44,24 @@
             return new PrivateKeyInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
         
     public PrivateKeyInfo(
         AlgorithmIdentifier algId,
         DERObject           privateKey)
     {
+        this(algId, privateKey, null);
+    }
+
+    public PrivateKeyInfo(
+        AlgorithmIdentifier algId,
+        DERObject           privateKey,
+        ASN1Set             attributes)
+    {
         this.privKey = privateKey;
         this.algId = algId;
+        this.attributes = attributes;
     }
 
     public PrivateKeyInfo(
@@ -101,7 +110,7 @@
     }
 
     /**
-     * write out an RSA private key with it's asscociated information
+     * write out an RSA private key with its associated information
      * as described in PKCS8.
      * <pre>
      *      PrivateKeyInfo ::= SEQUENCE {
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java b/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java
deleted file mode 100644
index 23508a4..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-
-public class RC2CBCParameter
-    extends ASN1Encodable
-{
-    DERInteger      version;
-    ASN1OctetString iv;
-
-    public static RC2CBCParameter getInstance(
-        Object  o)
-    {
-        if (o instanceof ASN1Sequence)
-        {
-            return new RC2CBCParameter((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in RC2CBCParameter factory");
-    }
-
-    public RC2CBCParameter(
-        byte[]  iv)
-    {
-        this.version = null;
-        this.iv = new DEROctetString(iv);
-    }
-
-    public RC2CBCParameter(
-        int     parameterVersion,
-        byte[]  iv)
-    {
-        this.version = new DERInteger(parameterVersion);
-        this.iv = new DEROctetString(iv);
-    }
-
-    public RC2CBCParameter(
-        ASN1Sequence  seq)
-    {
-        if (seq.size() == 1)
-        {
-            version = null;
-            iv = (ASN1OctetString)seq.getObjectAt(0);
-        }
-        else
-        {
-            version = (DERInteger)seq.getObjectAt(0);
-            iv = (ASN1OctetString)seq.getObjectAt(1);
-        }
-    }
-
-    public BigInteger getRC2ParameterVersion()
-    {
-        if (version == null)
-        {
-            return null;
-        }
-
-        return version.getValue();
-    }
-
-    public byte[] getIV()
-    {
-        return iv.getOctets();
-    }
-
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        if (version != null)
-        {
-            v.add(version);
-        }
-
-        v.add(iv);
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
index 87537ff..1ded6e9 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -20,7 +20,7 @@
     private AlgorithmIdentifier pSourceAlgorithm;
     
     // BEGIN android-changed
-    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.THE_ONE);
+    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
     // END android-changed
     public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
     public final static AlgorithmIdentifier DEFAULT_P_SOURCE_ALGORITHM = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0]));
@@ -37,7 +37,7 @@
             return new RSAESOAEPparams((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     /**
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java b/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
index 998b7c6..e2f0072 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
@@ -44,7 +44,7 @@
             return new RSAPrivateKeyStructure((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public RSAPrivateKeyStructure(
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java b/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
index 684aabd..bfa3ed6 100644
--- a/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
+++ b/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
@@ -21,7 +21,7 @@
     private DERInteger          trailerField;
     
     // BEGIN android-changed
-    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.THE_ONE);
+    public final static AlgorithmIdentifier DEFAULT_HASH_ALGORITHM = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
     // END android-changed
     public final static AlgorithmIdentifier DEFAULT_MASK_GEN_FUNCTION = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, DEFAULT_HASH_ALGORITHM);
     public final static DERInteger          DEFAULT_SALT_LENGTH = new DERInteger(20);
@@ -30,7 +30,7 @@
     public static RSASSAPSSparams getInstance(
         Object  obj)
     {
-        if (obj instanceof RSASSAPSSparams)
+        if (obj == null || obj instanceof RSASSAPSSparams)
         {
             return (RSASSAPSSparams)obj;
         }
@@ -39,7 +39,7 @@
             return new RSASSAPSSparams((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     /**
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java b/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java
deleted file mode 100644
index 8e4ccbb..0000000
--- a/src/main/java/org/bouncycastle/asn1/pkcs/SignerInfo.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package org.bouncycastle.asn1.pkcs;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.*;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-/**
- * a PKCS#7 signer info object.
- */
-public class SignerInfo
-    extends ASN1Encodable
-{
-    private DERInteger              version;
-    private IssuerAndSerialNumber   issuerAndSerialNumber;
-    private AlgorithmIdentifier     digAlgorithm;
-    private ASN1Set                 authenticatedAttributes;
-    private AlgorithmIdentifier     digEncryptionAlgorithm;
-    private ASN1OctetString         encryptedDigest;
-    private ASN1Set                 unauthenticatedAttributes;
-
-    public static SignerInfo getInstance(
-        Object  o)
-    {
-        if (o instanceof SignerInfo)
-        {
-            return (SignerInfo)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new SignerInfo((ASN1Sequence)o);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-    public SignerInfo(
-        DERInteger              version,
-        IssuerAndSerialNumber   issuerAndSerialNumber,
-        AlgorithmIdentifier     digAlgorithm,
-        ASN1Set                 authenticatedAttributes,
-        AlgorithmIdentifier     digEncryptionAlgorithm,
-        ASN1OctetString         encryptedDigest,
-        ASN1Set                 unauthenticatedAttributes)
-    {
-        this.version = version;
-        this.issuerAndSerialNumber = issuerAndSerialNumber;
-        this.digAlgorithm = digAlgorithm;
-        this.authenticatedAttributes = authenticatedAttributes;
-        this.digEncryptionAlgorithm = digEncryptionAlgorithm;
-        this.encryptedDigest = encryptedDigest;
-        this.unauthenticatedAttributes = unauthenticatedAttributes;
-    }
-
-    public SignerInfo(
-        ASN1Sequence seq)
-    {
-        Enumeration     e = seq.getObjects();
-
-        version = (DERInteger)e.nextElement();
-        issuerAndSerialNumber = IssuerAndSerialNumber.getInstance(e.nextElement());
-        digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
-
-        Object obj = e.nextElement();
-
-        if (obj instanceof ASN1TaggedObject)
-        {
-            authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false);
-
-            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
-        }
-        else
-        {
-            authenticatedAttributes = null;
-            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj);
-        }
-
-        encryptedDigest = DEROctetString.getInstance(e.nextElement());
-
-        if (e.hasMoreElements())
-        {
-            unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
-        }
-        else
-        {
-            unauthenticatedAttributes = null;
-        }
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public IssuerAndSerialNumber getIssuerAndSerialNumber()
-    {
-        return issuerAndSerialNumber;
-    }
-
-    public ASN1Set getAuthenticatedAttributes()
-    {
-        return authenticatedAttributes;
-    }
-
-    public AlgorithmIdentifier getDigestAlgorithm()
-    {
-        return digAlgorithm;
-    }
-
-    public ASN1OctetString getEncryptedDigest()
-    {
-        return encryptedDigest;
-    }
-
-    public AlgorithmIdentifier getDigestEncryptionAlgorithm()
-    {
-        return digEncryptionAlgorithm;
-    }
-
-    public ASN1Set getUnauthenticatedAttributes()
-    {
-        return unauthenticatedAttributes;
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     *  SignerInfo ::= SEQUENCE {
-     *      version Version,
-     *      issuerAndSerialNumber IssuerAndSerialNumber,
-     *      digestAlgorithm DigestAlgorithmIdentifier,
-     *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
-     *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
-     *      encryptedDigest EncryptedDigest,
-     *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
-     *  }
-     *
-     *  EncryptedDigest ::= OCTET STRING
-     *
-     *  DigestAlgorithmIdentifier ::= AlgorithmIdentifier
-     *
-     *  DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(issuerAndSerialNumber);
-        v.add(digAlgorithm);
-
-        if (authenticatedAttributes != null)
-        {
-            v.add(new DERTaggedObject(false, 0, authenticatedAttributes));
-        }
-
-        v.add(digEncryptionAlgorithm);
-        v.add(encryptedDigest);
-
-        if (unauthenticatedAttributes != null)
-        {
-            v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
index 58098e1..32a2ed6 100644
--- a/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
@@ -15,4 +15,28 @@
     static final DERObjectIdentifier    rsaSignatureWithripemd160           = new DERObjectIdentifier(teleTrusTRSAsignatureAlgorithm + ".2");
     static final DERObjectIdentifier    rsaSignatureWithripemd128           = new DERObjectIdentifier(teleTrusTRSAsignatureAlgorithm + ".3");
     static final DERObjectIdentifier    rsaSignatureWithripemd256           = new DERObjectIdentifier(teleTrusTRSAsignatureAlgorithm + ".4");
+
+    static final DERObjectIdentifier    ecSign = new DERObjectIdentifier(teleTrusTAlgorithm + ".3.2");
+
+    static final DERObjectIdentifier    ecSignWithSha1  = new DERObjectIdentifier(ecSign + ".1");
+    static final DERObjectIdentifier    ecSignWithRipemd160  = new DERObjectIdentifier(ecSign + ".2");
+
+    static final DERObjectIdentifier ecc_brainpool = new DERObjectIdentifier(teleTrusTAlgorithm + ".3.2.8");
+    static final DERObjectIdentifier ellipticCurve = new DERObjectIdentifier(ecc_brainpool + ".1");
+    static final DERObjectIdentifier versionOne = new DERObjectIdentifier(ellipticCurve + ".1");    
+
+    static final DERObjectIdentifier brainpoolP160r1 = new DERObjectIdentifier(versionOne + ".1");
+    static final DERObjectIdentifier brainpoolP160t1 = new DERObjectIdentifier(versionOne + ".2");
+    static final DERObjectIdentifier brainpoolP192r1 = new DERObjectIdentifier(versionOne + ".3");
+    static final DERObjectIdentifier brainpoolP192t1 = new DERObjectIdentifier(versionOne + ".4");
+    static final DERObjectIdentifier brainpoolP224r1 = new DERObjectIdentifier(versionOne + ".5");
+    static final DERObjectIdentifier brainpoolP224t1 = new DERObjectIdentifier(versionOne + ".6");
+    static final DERObjectIdentifier brainpoolP256r1 = new DERObjectIdentifier(versionOne + ".7");
+    static final DERObjectIdentifier brainpoolP256t1 = new DERObjectIdentifier(versionOne + ".8");
+    static final DERObjectIdentifier brainpoolP320r1 = new DERObjectIdentifier(versionOne + ".9");
+    static final DERObjectIdentifier brainpoolP320t1 = new DERObjectIdentifier(versionOne+".10");
+    static final DERObjectIdentifier brainpoolP384r1 = new DERObjectIdentifier(versionOne+".11");
+    static final DERObjectIdentifier brainpoolP384t1 = new DERObjectIdentifier(versionOne+".12");
+    static final DERObjectIdentifier brainpoolP512r1 = new DERObjectIdentifier(versionOne+".13");
+    static final DERObjectIdentifier brainpoolP512t1 = new DERObjectIdentifier(versionOne+".14");
 }
diff --git a/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java b/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java
deleted file mode 100644
index 18f4c15..0000000
--- a/src/main/java/org/bouncycastle/asn1/tsp/Accuracy.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package org.bouncycastle.asn1.tsp;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-
-
-public class Accuracy
-    extends ASN1Encodable
-{
-    DERInteger seconds;
-
-    DERInteger millis;
-
-    DERInteger micros;
-
-    // constantes
-    protected static final int MIN_MILLIS = 1;
-
-    protected static final int MAX_MILLIS = 999;
-
-    protected static final int MIN_MICROS = 1;
-
-    protected static final int MAX_MICROS = 999;
-
-    protected Accuracy()
-    {
-    }
-
-    public Accuracy(
-        DERInteger seconds,
-        DERInteger millis,
-        DERInteger micros)
-    {
-        this.seconds = seconds;
-
-        //Verifications
-        if (millis != null
-                && (millis.getValue().intValue() < MIN_MILLIS || millis
-                        .getValue().intValue() > MAX_MILLIS))
-        {
-            throw new IllegalArgumentException(
-                    "Invalid millis field : not in (1..999)");
-        }
-        else
-        {
-            this.millis = millis;
-        }
-
-        if (micros != null
-                && (micros.getValue().intValue() < MIN_MICROS || micros
-                        .getValue().intValue() > MAX_MICROS))
-        {
-            throw new IllegalArgumentException(
-                    "Invalid micros field : not in (1..999)");
-        }
-        else
-        {
-            this.micros = micros;
-        }
-
-    }
-
-    public Accuracy(ASN1Sequence seq)
-    {
-        seconds = null;
-        millis = null;
-        micros = null;
-
-        for (int i = 0; i < seq.size(); i++)
-        {
-            // seconds
-            if (seq.getObjectAt(i) instanceof DERInteger)
-            {
-                seconds = (DERInteger) seq.getObjectAt(i);
-            }
-            else if (seq.getObjectAt(i) instanceof DERTaggedObject)
-            {
-                DERTaggedObject extra = (DERTaggedObject) seq.getObjectAt(i);
-
-                switch (extra.getTagNo())
-                {
-                case 0:
-                    millis = DERInteger.getInstance(extra, false);
-                    if (millis.getValue().intValue() < MIN_MILLIS
-                            || millis.getValue().intValue() > MAX_MILLIS)
-                    {
-                        throw new IllegalArgumentException(
-                                "Invalid millis field : not in (1..999).");
-                    }
-                    break;
-                case 1:
-                    micros = DERInteger.getInstance(extra, false);
-                    if (micros.getValue().intValue() < MIN_MICROS
-                            || micros.getValue().intValue() > MAX_MICROS)
-                    {
-                        throw new IllegalArgumentException(
-                                "Invalid micros field : not in (1..999).");
-                    }
-                    break;
-                default:
-                    throw new IllegalArgumentException("Invalig tag number");
-                }
-            }
-        }
-    }
-
-    public static Accuracy getInstance(Object o)
-    {
-        if (o == null || o instanceof Accuracy)
-        {
-            return (Accuracy) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new Accuracy((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "Unknown object in 'Accuracy' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    public DERInteger getSeconds()
-    {
-        return seconds;
-    }
-
-    public DERInteger getMillis()
-    {
-        return millis;
-    }
-
-    public DERInteger getMicros()
-    {
-        return micros;
-    }
-
-    /**
-     * <pre>
-     * Accuracy ::= SEQUENCE {
-     *             seconds        INTEGER              OPTIONAL,
-     *             millis     [0] INTEGER  (1..999)    OPTIONAL,
-     *             micros     [1] INTEGER  (1..999)    OPTIONAL
-     *             }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        if (seconds != null)
-        {
-            v.add(seconds);
-        }
-        
-        if (millis != null)
-        {
-            v.add(new DERTaggedObject(false, 0, millis));
-        }
-        
-        if (micros != null)
-        {
-            v.add(new DERTaggedObject(false, 1, micros));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java b/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java
deleted file mode 100644
index 46b8dc1..0000000
--- a/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.bouncycastle.asn1.tsp;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-public class MessageImprint
-    extends ASN1Encodable
-{
-    AlgorithmIdentifier hashAlgorithm;
-    byte[]              hashedMessage;
-    
-    /**
-     * @param o
-     * @return a MessageImprint object.
-     */
-    public static MessageImprint getInstance(Object o)
-    {
-        if (o == null || o instanceof MessageImprint)
-        {
-            return (MessageImprint)o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new MessageImprint((ASN1Sequence)o);
-        }
-        
-        throw new IllegalArgumentException("Bad object in factory.");
-    }
-    
-    public MessageImprint(
-        ASN1Sequence seq)
-    {
-        this.hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
-        this.hashedMessage = ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets();
-    }
-    
-    public MessageImprint(
-        AlgorithmIdentifier hashAlgorithm,
-        byte[]              hashedMessage)
-    {
-        this.hashAlgorithm = hashAlgorithm;
-        this.hashedMessage = hashedMessage;
-    }
-    
-    public AlgorithmIdentifier getHashAlgorithm()
-    {
-        return hashAlgorithm;
-    }
-    
-    public byte[] getHashedMessage()
-    {
-        return hashedMessage;
-    }
-    
-    /**
-     * <pre>
-     *    MessageImprint ::= SEQUENCE  {
-     *       hashAlgorithm                AlgorithmIdentifier,
-     *       hashedMessage                OCTET STRING  }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(hashAlgorithm);
-        v.add(new DEROctetString(hashedMessage));
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java b/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java
deleted file mode 100644
index e81ea3a..0000000
--- a/src/main/java/org/bouncycastle/asn1/tsp/TSTInfo.java
+++ /dev/null
@@ -1,256 +0,0 @@
-package org.bouncycastle.asn1.tsp;
-
-import java.io.IOException;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBoolean;
-import org.bouncycastle.asn1.DEREncodableVector;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class TSTInfo
-    extends ASN1Encodable
-{
-    DERInteger version;
-
-    DERObjectIdentifier tsaPolicyId;
-
-    MessageImprint messageImprint;
-
-    DERInteger serialNumber;
-
-    DERGeneralizedTime genTime;
-
-    Accuracy accuracy;
-
-    DERBoolean ordering;
-
-    DERInteger nonce;
-
-    GeneralName tsa;
-
-    X509Extensions extensions;
-
-    public static TSTInfo getInstance(Object o)
-    {
-        if (o == null || o instanceof TSTInfo)
-        {
-            return (TSTInfo) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new TSTInfo((ASN1Sequence) o);
-        }
-        else if (o instanceof ASN1OctetString)
-        {
-            try
-            {
-                return getInstance(new ASN1InputStream(((ASN1OctetString)o).getOctets()).readObject());
-            }
-            catch (IOException ioEx)
-            {
-                throw new IllegalArgumentException(
-                        "Bad object format in 'TSTInfo' factory.");
-            }
-        }
-
-        throw new IllegalArgumentException(
-                "Unknown object in 'TSTInfo' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    public TSTInfo(ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        // version
-        version = DERInteger.getInstance(e.nextElement());
-
-        // tsaPolicy
-        tsaPolicyId = DERObjectIdentifier.getInstance(e.nextElement());
-
-        // messageImprint
-        messageImprint = MessageImprint.getInstance(e.nextElement());
-
-        // serialNumber
-        serialNumber = DERInteger.getInstance(e.nextElement());
-
-        // genTime
-        genTime = DERGeneralizedTime.getInstance(e.nextElement());
-
-        // default for ordering
-        // BEGIN android-changed
-        ordering = DERBoolean.FALSE;
-        // END android-changed
-        
-        while (e.hasMoreElements())
-        {
-            DERObject o = (DERObject) e.nextElement();
-
-            if (o instanceof ASN1TaggedObject)
-            {
-                DERTaggedObject tagged = (DERTaggedObject) o;
-
-                switch (tagged.getTagNo())
-                {
-                case 0:
-                    tsa = GeneralName.getInstance(tagged, true);
-                    break;
-                case 1:
-                    extensions = X509Extensions.getInstance(tagged, false);
-                    break;
-                default:
-                    throw new IllegalArgumentException("Unknown tag value " + tagged.getTagNo());
-                }
-            }
-            else if (o instanceof DERSequence)
-            {
-                accuracy = Accuracy.getInstance(o);
-            }
-            else if (o instanceof DERBoolean)
-            {
-                ordering = DERBoolean.getInstance(o);
-            }
-            else if (o instanceof DERInteger)
-            {
-                nonce = DERInteger.getInstance(o);
-            }
-
-        }
-    }
-
-    public TSTInfo(DERObjectIdentifier tsaPolicyId, MessageImprint messageImprint,
-            DERInteger serialNumber, DERGeneralizedTime genTime,
-            Accuracy accuracy, DERBoolean ordering, DERInteger nonce,
-            GeneralName tsa, X509Extensions extensions)
-    {
-        version = new DERInteger(1);
-        this.tsaPolicyId = tsaPolicyId;
-        this.messageImprint = messageImprint;
-        this.serialNumber = serialNumber;
-        this.genTime = genTime;
-
-        this.accuracy = accuracy;
-        this.ordering = ordering;
-        this.nonce = nonce;
-        this.tsa = tsa;
-        this.extensions = extensions;
-    }
-
-    public MessageImprint getMessageImprint()
-    {
-        return messageImprint;
-    }
-
-    public DERObjectIdentifier getPolicy()
-    {
-        return tsaPolicyId;
-    }
-
-    public DERInteger getSerialNumber()
-    {
-        return serialNumber;
-    }
-
-    public Accuracy getAccuracy()
-    {
-        return accuracy;
-    }
-
-    public DERGeneralizedTime getGenTime()
-    {
-        return genTime;
-    }
-
-    public DERBoolean getOrdering()
-    {
-        return ordering;
-    }
-
-    public DERInteger getNonce()
-    {
-        return nonce;
-    }
-
-    public GeneralName getTsa()
-    {
-        return tsa;
-    }
-
-    public X509Extensions getExtensions()
-    {
-        return extensions;
-    }
-
-    /**
-     * <pre>
-     * 
-     *     TSTInfo ::= SEQUENCE  {
-     *        version                      INTEGER  { v1(1) },
-     *        policy                       TSAPolicyId,
-     *        messageImprint               MessageImprint,
-     *          -- MUST have the same value as the similar field in
-     *          -- TimeStampReq
-     *        serialNumber                 INTEGER,
-     *         -- Time-Stamping users MUST be ready to accommodate integers
-     *         -- up to 160 bits.
-     *        genTime                      GeneralizedTime,
-     *        accuracy                     Accuracy                 OPTIONAL,
-     *        ordering                     BOOLEAN             DEFAULT FALSE,
-     *        nonce                        INTEGER                  OPTIONAL,
-     *          -- MUST be present if the similar field was present
-     *          -- in TimeStampReq.  In that case it MUST have the same value.
-     *        tsa                          [0] GeneralName          OPTIONAL,
-     *        extensions                   [1] IMPLICIT Extensions   OPTIONAL  }
-     * 
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        DEREncodableVector seq = new DEREncodableVector();
-        seq.add(version);
-
-        seq.add(tsaPolicyId);
-        seq.add(messageImprint);
-        seq.add(serialNumber);
-        seq.add(genTime);
-
-        if (accuracy != null)
-        {
-            seq.add(accuracy);
-        }
-        
-        if (ordering != null && ordering.isTrue())
-        {
-            seq.add(ordering);
-        }
-        
-        if (nonce != null)
-        {
-            seq.add(nonce);
-        }
-        
-        if (tsa != null)
-        {
-            seq.add(new DERTaggedObject(true, 0, tsa));
-        }
-        
-        if (extensions != null)
-        {
-            seq.add(new DERTaggedObject(false, 1, extensions));
-        }
-
-        return new DERSequence(seq);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java b/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java
deleted file mode 100644
index 46565e7..0000000
--- a/src/main/java/org/bouncycastle/asn1/tsp/TimeStampReq.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package org.bouncycastle.asn1.tsp;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBoolean;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-public class TimeStampReq
-    extends ASN1Encodable
-{
-    DERInteger version;
-
-    MessageImprint messageImprint;
-
-    DERObjectIdentifier tsaPolicy;
-
-    DERInteger nonce;
-
-    DERBoolean certReq;
-
-    X509Extensions extensions;
-
-    public static TimeStampReq getInstance(Object o)
-    {
-        if (o == null || o instanceof TimeStampReq)
-        {
-            return (TimeStampReq) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new TimeStampReq((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "Unknown object in 'TimeStampReq' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    public TimeStampReq(ASN1Sequence seq)
-    {
-        int nbObjects = seq.size();
-
-        int seqStart = 0;
-
-        // version
-        version = DERInteger.getInstance(seq.getObjectAt(seqStart));
-
-        seqStart++;
-
-        // messageImprint
-        messageImprint = MessageImprint.getInstance(seq.getObjectAt(seqStart));
-
-        seqStart++;
-
-        for (int opt = seqStart; opt < nbObjects; opt++)
-        {
-            // tsaPolicy
-            if (seq.getObjectAt(opt) instanceof DERObjectIdentifier)
-            {
-                tsaPolicy = DERObjectIdentifier.getInstance(seq.getObjectAt(opt));
-            }
-            // nonce
-            else if (seq.getObjectAt(opt) instanceof DERInteger)
-            {
-                nonce = DERInteger.getInstance(seq.getObjectAt(opt));
-            }
-            // certReq
-            else if (seq.getObjectAt(opt) instanceof DERBoolean)
-            {
-                certReq = DERBoolean.getInstance(seq.getObjectAt(opt));
-            }
-            // extensions
-            else if (seq.getObjectAt(opt) instanceof ASN1TaggedObject)
-            {
-                ASN1TaggedObject    tagged = (ASN1TaggedObject)seq.getObjectAt(opt);
-                if (tagged.getTagNo() == 0)
-                {
-                    extensions = X509Extensions.getInstance(tagged, false);
-                }
-            }
-        }
-    }
-
-    public TimeStampReq(
-        MessageImprint      messageImprint,
-        DERObjectIdentifier tsaPolicy,
-        DERInteger          nonce,
-        DERBoolean          certReq,
-        X509Extensions      extensions)
-    {
-        // default
-        version = new DERInteger(1);
-
-        this.messageImprint = messageImprint;
-        this.tsaPolicy = tsaPolicy;
-        this.nonce = nonce;
-        this.certReq = certReq;
-        this.extensions = extensions;
-    }
-
-    public DERInteger getVersion()
-    {
-        return version;
-    }
-
-    public MessageImprint getMessageImprint()
-    {
-        return messageImprint;
-    }
-
-    public DERObjectIdentifier getReqPolicy()
-    {
-        return tsaPolicy;
-    }
-
-    public DERInteger getNonce()
-    {
-        return nonce;
-    }
-
-    public DERBoolean getCertReq()
-    {
-        return certReq;
-    }
-
-    public X509Extensions getExtensions()
-    {
-        return extensions;
-    }
-
-    /**
-     * <pre>
-     * TimeStampReq ::= SEQUENCE  {
-     *  version                      INTEGER  { v1(1) },
-     *  messageImprint               MessageImprint,
-     *    --a hash algorithm OID and the hash value of the data to be
-     *    --time-stamped
-     *  reqPolicy             TSAPolicyId              OPTIONAL,
-     *  nonce                 INTEGER                  OPTIONAL,
-     *  certReq               BOOLEAN                  DEFAULT FALSE,
-     *  extensions            [0] IMPLICIT Extensions  OPTIONAL
-     * }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        v.add(version);
-        v.add(messageImprint);
-        
-        if (tsaPolicy != null)
-        {
-            v.add(tsaPolicy);
-        }
-        
-        if (nonce != null)
-        {
-            v.add(nonce);
-        }
-        
-        if (certReq != null && certReq.isTrue())
-        {
-            v.add(certReq);
-        }
-        
-        if (extensions != null)
-        {
-            v.add(new DERTaggedObject(false, 0, extensions));
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java b/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java
deleted file mode 100644
index f5bfa7e..0000000
--- a/src/main/java/org/bouncycastle/asn1/tsp/TimeStampResp.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.bouncycastle.asn1.tsp;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.asn1.cmp.PKIStatusInfo;
-
-
-public class TimeStampResp
-    extends ASN1Encodable
-{
-    PKIStatusInfo pkiStatusInfo;
-
-    ContentInfo timeStampToken;
-
-    public static TimeStampResp getInstance(Object o)
-    {
-        if (o == null || o instanceof TimeStampResp)
-        {
-            return (TimeStampResp) o;
-        }
-        else if (o instanceof ASN1Sequence)
-        {
-            return new TimeStampResp((ASN1Sequence) o);
-        }
-
-        throw new IllegalArgumentException(
-                "unknown object in 'TimeStampResp' factory : "
-                        + o.getClass().getName() + ".");
-    }
-
-    public TimeStampResp(ASN1Sequence seq)
-    {
-
-        Enumeration e = seq.getObjects();
-
-        // status
-        pkiStatusInfo = PKIStatusInfo.getInstance(e.nextElement());
-
-        if (e.hasMoreElements())
-        {
-            timeStampToken = ContentInfo.getInstance(e.nextElement());
-        }
-    }
-
-    public TimeStampResp(PKIStatusInfo pkiStatusInfo, ContentInfo timeStampToken)
-    {
-        this.pkiStatusInfo = pkiStatusInfo;
-        this.timeStampToken = timeStampToken;
-    }
-
-    public PKIStatusInfo getStatus()
-    {
-        return pkiStatusInfo;
-    }
-
-    public ContentInfo getTimeStampToken()
-    {
-        return timeStampToken;
-    }
-
-    /**
-     * <pre>
-     * TimeStampResp ::= SEQUENCE  {
-     *   status                  PKIStatusInfo,
-     *   timeStampToken          TimeStampToken     OPTIONAL  }
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        
-        v.add(pkiStatusInfo);
-        if (timeStampToken != null)
-        {
-            v.add(timeStampToken);
-        }
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
index 97dca60..7d4f999 100644
--- a/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
+++ b/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -1,26 +1,64 @@
 package org.bouncycastle.asn1.util;
 
+import java.io.IOException;
 import java.util.Enumeration;
 
-import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BERApplicationSpecific;
+import org.bouncycastle.asn1.BERConstructedOctetString;
+import org.bouncycastle.asn1.BERConstructedSequence;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERSet;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.DERApplicationSpecific;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DERConstructedSequence;
+import org.bouncycastle.asn1.DERConstructedSet;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERExternal;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERT61String;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERTags;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.DERUnknownTag;
+import org.bouncycastle.asn1.DERVisibleString;
 import org.bouncycastle.util.encoders.Hex;
 
 public class ASN1Dump
 {
-    private static String  TAB = "    ";
+    private static final String  TAB = "    ";
+    private static final int SAMPLE_SIZE = 32;
 
     /**
      * dump a DER object as a formatted string with indentation
      *
      * @param obj the DERObject to be dumped out.
      */
-    static String _dumpAsString(
+    static void _dumpAsString(
         String      indent,
-        DERObject   obj)
+        boolean     verbose,
+        DERObject   obj,
+        StringBuffer    buf)
     {
+        String nl = System.getProperty("line.separator");
         if (obj instanceof ASN1Sequence)
         {
-            StringBuffer    buf = new StringBuffer();
             Enumeration     e = ((ASN1Sequence)obj).getObjects();
             String          tab = indent + TAB;
 
@@ -46,34 +84,32 @@
                 buf.append("Sequence");
             }
 
-            buf.append(System.getProperty("line.separator"));
+            buf.append(nl);
 
             while (e.hasMoreElements())
             {
                 Object  o = e.nextElement();
 
                 // BEGIN android-changed
-                if (o == null || o.equals(DERNull.THE_ONE))
+                if (o == null || o.equals(DERNull.INSTANCE))
+                // END android-changed
                 {
                     buf.append(tab);
                     buf.append("NULL");
-                    buf.append(System.getProperty("line.separator"));
+                    buf.append(nl);
                 }
                 else if (o instanceof DERObject)
                 {
-                    buf.append(_dumpAsString(tab, (DERObject)o));
+                    _dumpAsString(tab, verbose, (DERObject)o, buf);
                 }
                 else
                 {
-                    buf.append(_dumpAsString(tab, ((DEREncodable)o).getDERObject()));
+                    _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf);
                 }
-                // END android-changed
             }
-            return buf.toString();
         }
         else if (obj instanceof DERTaggedObject)
         {
-            StringBuffer    buf = new StringBuffer();
             String          tab = indent + TAB;
 
             buf.append(indent);
@@ -96,30 +132,27 @@
                 buf.append(" IMPLICIT ");
             }
 
-            buf.append(System.getProperty("line.separator"));
+            buf.append(nl);
 
             if (o.isEmpty())
             {
                 buf.append(tab);
                 buf.append("EMPTY");
-                buf.append(System.getProperty("line.separator"));
+                buf.append(nl);
             }
             else
             {
-                buf.append(_dumpAsString(tab, o.getObject()));
+                _dumpAsString(tab, verbose, o.getObject(), buf);
             }
-
-            return buf.toString();
         }
         else if (obj instanceof DERConstructedSet)
         {
-            StringBuffer    buf = new StringBuffer();
             Enumeration     e = ((ASN1Set)obj).getObjects();
             String          tab = indent + TAB;
 
             buf.append(indent);
             buf.append("ConstructedSet");
-            buf.append(System.getProperty("line.separator"));
+            buf.append(nl);
 
             while (e.hasMoreElements())
             {
@@ -129,28 +162,26 @@
                 {
                     buf.append(tab);
                     buf.append("NULL");
-                    buf.append(System.getProperty("line.separator"));
+                    buf.append(nl);
                 }
                 else if (o instanceof DERObject)
                 {
-                    buf.append(_dumpAsString(tab, (DERObject)o));
+                    _dumpAsString(tab, verbose, (DERObject)o, buf);
                 }
                 else
                 {
-                    buf.append(_dumpAsString(tab, ((DEREncodable)o).getDERObject()));
+                    _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf);
                 }
             }
-            return buf.toString();
         }
         else if (obj instanceof BERSet)
         {
-            StringBuffer    buf = new StringBuffer();
             Enumeration     e = ((ASN1Set)obj).getObjects();
             String          tab = indent + TAB;
 
             buf.append(indent);
             buf.append("BER Set");
-            buf.append(System.getProperty("line.separator"));
+            buf.append(nl);
 
             while (e.hasMoreElements())
             {
@@ -160,28 +191,26 @@
                 {
                     buf.append(tab);
                     buf.append("NULL");
-                    buf.append(System.getProperty("line.separator"));
+                    buf.append(nl);
                 }
                 else if (o instanceof DERObject)
                 {
-                    buf.append(_dumpAsString(tab, (DERObject)o));
+                    _dumpAsString(tab, verbose, (DERObject)o, buf);
                 }
                 else
                 {
-                    buf.append(_dumpAsString(tab, ((DEREncodable)o).getDERObject()));
+                    _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf);
                 }
             }
-            return buf.toString();
         }
         else if (obj instanceof DERSet)
         {
-            StringBuffer    buf = new StringBuffer();
             Enumeration     e = ((ASN1Set)obj).getObjects();
             String          tab = indent + TAB;
 
             buf.append(indent);
             buf.append("DER Set");
-            buf.append(System.getProperty("line.separator"));
+            buf.append(nl);
 
             while (e.hasMoreElements())
             {
@@ -191,102 +220,255 @@
                 {
                     buf.append(tab);
                     buf.append("NULL");
-                    buf.append(System.getProperty("line.separator"));
+                    buf.append(nl);
                 }
                 else if (o instanceof DERObject)
                 {
-                    buf.append(_dumpAsString(tab, (DERObject)o));
+                    _dumpAsString(tab, verbose, (DERObject)o, buf);
                 }
                 else
                 {
-                    buf.append(_dumpAsString(tab, ((DEREncodable)o).getDERObject()));
+                    _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf);
                 }
             }
-            return buf.toString();
         }
         else if (obj instanceof DERObjectIdentifier)
         {
-            return indent + "ObjectIdentifier(" + ((DERObjectIdentifier)obj).getId() + ")" + System.getProperty("line.separator");
+            buf.append(indent + "ObjectIdentifier(" + ((DERObjectIdentifier)obj).getId() + ")" + nl);
         }
         else if (obj instanceof DERBoolean)
         {
-            return indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + System.getProperty("line.separator");
+            buf.append(indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + nl);
         }
         else if (obj instanceof DERInteger)
         {
-            return indent + "Integer(" + ((DERInteger)obj).getValue() + ")" + System.getProperty("line.separator");
+            buf.append(indent + "Integer(" + ((DERInteger)obj).getValue() + ")" + nl);
         }
         else if (obj instanceof BERConstructedOctetString)
         {
-            return indent + "BER Constructed Octet String" + "[" + ((ASN1OctetString)obj).getOctets().length + "] " + System.getProperty("line.separator");
+            ASN1OctetString oct = (ASN1OctetString)obj;
+            buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] ");
+            if (verbose)
+            {
+                buf.append(dumpBinaryDataAsString(indent, oct.getOctets()));
+            }
+            else{
+                buf.append(nl);
+            }
         }
         else if (obj instanceof DEROctetString)
         {
-            return indent + "DER Octet String" + "[" + ((ASN1OctetString)obj).getOctets().length + "] " + System.getProperty("line.separator");
+            ASN1OctetString oct = (ASN1OctetString)obj;
+            buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] ");
+            if (verbose)
+            {
+                buf.append(dumpBinaryDataAsString(indent, oct.getOctets()));
+            }
+            else{
+                buf.append(nl);
+            }
         }
         else if (obj instanceof DERBitString)
         {
-            return indent + "DER Bit String" + "[" + ((DERBitString)obj).getBytes().length + ", " + ((DERBitString)obj).getPadBits() + "] " + System.getProperty("line.separator");
+            DERBitString bt = (DERBitString)obj;
+            buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
+            if (verbose)
+            {
+                buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
+            }
+            else{
+                buf.append(nl);
+            }
         }
         else if (obj instanceof DERIA5String)
         {
-            return indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERUTF8String)
         {
-            return indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERPrintableString)
         {
-            return indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERVisibleString)
         {
-            return indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERBMPString)
         {
-            return indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERT61String)
         {
-            return indent + "T61String(" + ((DERT61String)obj).getString() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
         }
         else if (obj instanceof DERUTCTime)
         {
-            return indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + nl);
         }
         else if (obj instanceof DERGeneralizedTime)
         {
-            return indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + System.getProperty("line.separator");
+            buf.append(indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + nl);
         }
         else if (obj instanceof DERUnknownTag)
         {
-            return indent + "Unknown " + Integer.toString(((DERUnknownTag)obj).getTag(), 16) + " " + new String(Hex.encode(((DERUnknownTag)obj).getData())) + System.getProperty("line.separator");
+            buf.append(indent + "Unknown " + Integer.toString(((DERUnknownTag)obj).getTag(), 16) + " " + new String(Hex.encode(((DERUnknownTag)obj).getData())) + nl);
+        }
+        else if (obj instanceof BERApplicationSpecific)
+        {
+            buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
+        }
+        else if (obj instanceof DERApplicationSpecific)
+        {
+            buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
+        }
+        else if (obj instanceof DEREnumerated)
+        {
+            DEREnumerated en = (DEREnumerated) obj;
+            buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
+        }
+        else if (obj instanceof DERExternal)
+        {
+            DERExternal ext = (DERExternal) obj;
+            buf.append(indent + "External " + nl);
+            String          tab = indent + TAB;
+            if (ext.getDirectReference() != null)
+            {
+                buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl);
+            }
+            if (ext.getIndirectReference() != null)
+            {
+                buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl);
+            }
+            if (ext.getDataValueDescriptor() != null)
+            {
+                _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf);
+            }
+            buf.append(tab + "Encoding: " + ext.getEncoding() + nl);
+            _dumpAsString(tab, verbose, ext.getExternalContent(), buf);
         }
         else
         {
-            return indent + obj.toString() + System.getProperty("line.separator");
+            buf.append(indent + obj.toString() + nl);
         }
     }
+    
+    private static String outputApplicationSpecific(String type, String indent, boolean verbose, DERObject obj, String nl)
+    {
+        DERApplicationSpecific app = (DERApplicationSpecific)obj;
+        StringBuffer buf = new StringBuffer();
+
+        if (app.isConstructed())
+        {
+            try
+            {
+                ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(DERTags.SEQUENCE));
+                buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
+                for (Enumeration e = s.getObjects(); e.hasMoreElements();)
+                {
+                    _dumpAsString(indent + TAB, verbose, (DERObject)e.nextElement(), buf);
+                }
+            }
+            catch (IOException e)
+            {
+                buf.append(e);
+            }
+            return buf.toString();
+        }
+
+        return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl;
+    }
 
     /**
-     * dump out a DER object as a formatted string
+     * dump out a DER object as a formatted string, in non-verbose mode.
      *
      * @param obj the DERObject to be dumped out.
+     * @return  the resulting string.
      */
     public static String dumpAsString(
         Object   obj)
     {
+        return dumpAsString(obj, false);
+    }
+
+    /**
+     * Dump out the object as a string.
+     *
+     * @param obj  the object to be dumped
+     * @param verbose  if true, dump out the contents of octet and bit strings.
+     * @return  the resulting string.
+     */
+    public static String dumpAsString(
+        Object   obj,
+        boolean  verbose)
+    {
+        StringBuffer buf = new StringBuffer();
+
         if (obj instanceof DERObject)
         {
-            return _dumpAsString("", (DERObject)obj);
+            _dumpAsString("", verbose, (DERObject)obj, buf);
         }
         else if (obj instanceof DEREncodable)
         {
-            return _dumpAsString("", ((DEREncodable)obj).getDERObject());
+            _dumpAsString("", verbose, ((DEREncodable)obj).getDERObject(), buf);
+        }
+        else
+        {
+            return "unknown object type " + obj.toString();
         }
 
-        return "unknown object type " + obj.toString();
+        return buf.toString();
+    }
+
+    private static String dumpBinaryDataAsString(String indent, byte[] bytes)
+    {
+        String nl = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+
+        indent += TAB;
+        
+        buf.append(nl);
+        for (int i = 0; i < bytes.length; i += SAMPLE_SIZE)
+        {
+            if (bytes.length - i > SAMPLE_SIZE)
+            {
+                buf.append(indent);
+                buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE)));
+                buf.append(TAB);
+                buf.append(calculateAscString(bytes, i, SAMPLE_SIZE));
+                buf.append(nl);
+            }
+            else
+            {
+                buf.append(indent);
+                buf.append(new String(Hex.encode(bytes, i, bytes.length - i)));
+                for (int j = bytes.length - i; j != SAMPLE_SIZE; j++)
+                {
+                    buf.append("  ");
+                }
+                buf.append(TAB);
+                buf.append(calculateAscString(bytes, i, bytes.length - i));
+                buf.append(nl);
+            }
+        }
+        
+        return buf.toString();
+    }
+
+    private static String calculateAscString(byte[] bytes, int off, int len)
+    {
+        StringBuffer buf = new StringBuffer();
+
+        for (int i = off; i != off + len; i++)
+        {
+            if (bytes[i] >= ' ' && bytes[i] <= '~')
+            {
+                buf.append((char)bytes[i]);
+            }
+        }
+
+        return buf.toString();
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/util/DERDump.java b/src/main/java/org/bouncycastle/asn1/util/DERDump.java
deleted file mode 100644
index e9d307a..0000000
--- a/src/main/java/org/bouncycastle/asn1/util/DERDump.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.bouncycastle.asn1.util;
-
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-
-/**
- * @deprecated use ASN1Dump.
- */
-public class DERDump
-    extends ASN1Dump
-{
-    /**
-     * dump out a DER object as a formatted string
-     *
-     * @param obj the DERObject to be dumped out.
-     */
-    public static String dumpAsString(
-        DERObject   obj)
-    {
-        return _dumpAsString("", obj);
-    }
-
-    /**
-     * dump out a DER object as a formatted string
-     *
-     * @param obj the DERObject to be dumped out.
-     */
-    public static String dumpAsString(
-        DEREncodable   obj)
-    {
-        return _dumpAsString("", obj.getDERObject());
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/util/Dump.java b/src/main/java/org/bouncycastle/asn1/util/Dump.java
deleted file mode 100644
index 27a37f3..0000000
--- a/src/main/java/org/bouncycastle/asn1/util/Dump.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.bouncycastle.asn1.util;
-
-import java.io.FileInputStream;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-
-public class Dump
-{
-    public static void main(
-        String args[])
-        throws Exception
-    {
-        FileInputStream fIn = new FileInputStream(args[0]);
-        ASN1InputStream bIn = new ASN1InputStream(fIn);
-        Object          obj = null;
-
-        while ((obj = bIn.readObject()) != null)
-        {
-            System.out.println(ASN1Dump.dumpAsString(obj));
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java b/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java
deleted file mode 100644
index ae13f55..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/AccessDescription.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The AccessDescription object.
- * <pre>
- * AccessDescription  ::=  SEQUENCE {
- *       accessMethod          OBJECT IDENTIFIER,
- *       accessLocation        GeneralName  }
- * </pre>
- */
-public class AccessDescription
-    extends ASN1Encodable
-{
-    public final static DERObjectIdentifier id_ad_caIssuers = new DERObjectIdentifier("1.3.6.1.5.5.7.48.2");
-    
-    public final static DERObjectIdentifier id_ad_ocsp = new DERObjectIdentifier("1.3.6.1.5.5.7.48.1");
-        
-    DERObjectIdentifier accessMethod = null;
-    GeneralName accessLocation = null;
-
-    public static AccessDescription getInstance(
-        Object  obj)
-    {
-        if (obj instanceof AccessDescription)
-        {
-            return (AccessDescription)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new AccessDescription((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
- 
-    public AccessDescription(
-        ASN1Sequence   seq)
-    {
-        if (seq.size() != 2) 
-        {
-            throw new IllegalArgumentException("wrong number of elements in inner sequence");
-        }
-        
-        accessMethod = DERObjectIdentifier.getInstance(seq.getObjectAt(0));
-        accessLocation = GeneralName.getInstance(seq.getObjectAt(1));
-    }
-
-    /**
-     * create an AccessDescription with the oid and location provided.
-     */
-    public AccessDescription(
-        DERObjectIdentifier oid,
-        GeneralName location)
-    {
-        accessMethod = oid;
-        accessLocation = location;
-    }
-
-    /**
-     * 
-     * @return the access method.
-     */
-    public DERObjectIdentifier getAccessMethod()
-    {
-        return accessMethod;
-    }
-    
-    /**
-     * 
-     * @return the access location
-     */
-    public GeneralName getAccessLocation()
-    {
-        return accessLocation;
-    }
-    
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector accessDescription  = new ASN1EncodableVector();
-        
-        accessDescription.add(accessMethod);
-        accessDescription.add(accessLocation);
-
-        return new DERSequence(accessDescription);
-    }
-
-    public String toString()
-    {
-        return ("AccessDescription: Oid(" + this.accessMethod.getId() + ")");
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
index 501dc9c..d581967 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
@@ -46,7 +46,7 @@
             return new AlgorithmIdentifier((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public AlgorithmIdentifier(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java b/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
index c973b37..2d67a51 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
@@ -13,7 +13,7 @@
 {
     ASN1Encodable   obj;
     DERObject       choiceObj;
-
+    
     public static AttCertIssuer getInstance(
         Object  obj)
     {
@@ -38,7 +38,7 @@
             return new AttCertIssuer(GeneralNames.getInstance(obj));
         }
 
-        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass());
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public static AttCertIssuer getInstance(
@@ -49,7 +49,8 @@
     }
 
     /**
-     * Don't use this one if you are trying to be RFC compliant.
+     * Don't use this one if you are trying to be RFC 3281 compliant.
+     * Use it for v1 attribute certificates only.
      * 
      * @param names our GeneralNames structure
      */
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java b/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
index de4e684..a4ab4cd 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
@@ -25,7 +25,7 @@
             return new AttCertValidityPeriod((ASN1Sequence)obj);
         }
         
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public AttCertValidityPeriod(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Attribute.java b/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
index c8cbcf7..c102e15 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
@@ -33,7 +33,7 @@
             return new Attribute((ASN1Sequence)o);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
     }
     
     public Attribute(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
index 6dcb7c6..cf00230 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -29,7 +29,7 @@
             return new AttributeCertificate((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public AttributeCertificate(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
index 74770f5..59ff27a 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -41,7 +41,7 @@
             return new AttributeCertificateInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public AttributeCertificateInfo(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java b/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java
deleted file mode 100644
index 9f34436..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/AuthorityInformationAccess.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The AuthorityInformationAccess object.
- * <pre>
- * id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
- *
- * AuthorityInfoAccessSyntax  ::=
- *      SEQUENCE SIZE (1..MAX) OF AccessDescription
- * AccessDescription  ::=  SEQUENCE {
- *       accessMethod          OBJECT IDENTIFIER,
- *       accessLocation        GeneralName  }
- *
- * id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
- * id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
- * id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
- * </pre>
- */
-public class AuthorityInformationAccess
-    extends ASN1Encodable
-{
-    private AccessDescription[]    descriptions;
-
-    public static AuthorityInformationAccess getInstance(
-        Object  obj)
-    {
-        if (obj instanceof AuthorityInformationAccess)
-        {
-            return (AuthorityInformationAccess)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new AuthorityInformationAccess((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
- 
-    public AuthorityInformationAccess(
-        ASN1Sequence   seq)
-    {
-        descriptions = new AccessDescription[seq.size()];
-        
-        for (int i = 0; i != seq.size(); i++)
-        {
-            descriptions[i] = AccessDescription.getInstance(seq.getObjectAt(i));
-        }
-    }
-
-    /**
-     * create an AuthorityInformationAccess with the oid and location provided.
-     */
-    public AuthorityInformationAccess(
-        DERObjectIdentifier oid,
-        GeneralName location)
-    {
-        descriptions = new AccessDescription[1];
-        
-        descriptions[0] = new AccessDescription(oid, location);
-    }
-
-
-    /**
-     * 
-     * @return the access descriptions contained in this object.
-     */
-    public AccessDescription[] getAccessDescriptions()
-    {
-        return descriptions;
-    }
-    
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector vec = new ASN1EncodableVector();
-        
-        for (int i = 0; i != descriptions.length; i++)
-        {
-            vec.add(descriptions[i]);
-        }
-        
-        return new DERSequence(vec);
-    }
-
-    public String toString()
-    {
-        return ("AuthorityInformationAccess: Oid(" + this.descriptions[0].getAccessMethod().getId() + ")");
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index 2e5107b..a3e5a54 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -1,12 +1,21 @@
 package org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 
+import java.math.BigInteger;
+import java.util.Enumeration;
+
 /**
  * The AuthorityKeyIdentifier object.
  * <pre>
@@ -42,12 +51,16 @@
         {
             return (AuthorityKeyIdentifier)obj;
         }
-        else if (obj instanceof ASN1Sequence)
+        if (obj instanceof ASN1Sequence)
         {
             return new AuthorityKeyIdentifier((ASN1Sequence)obj);
         }
+        if (obj instanceof X509Extension)
+        {
+            return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
+        }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public AuthorityKeyIdentifier(
@@ -136,6 +149,17 @@
     }
 
     /**
+      * create an AuthorityKeyIdentifier with a precomupted key identifier
+      */
+     public AuthorityKeyIdentifier(
+         byte[]                  keyIdentifier)
+     {
+         this.keyidentifier = new DEROctetString(keyIdentifier);
+         this.certissuer = null;
+         this.certserno = null;
+     }
+
+    /**
      * create an AuthorityKeyIdentifier with a precomupted key identifier
      * and the GeneralNames tag and the serial number provided as well.
      */
diff --git a/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java b/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
index c97b6cd..08a0c04 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
@@ -1,16 +1,16 @@
 package org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERBoolean;
-import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERSequence;
 
+import java.math.BigInteger;
+
 public class BasicConstraints
     extends ASN1Encodable
 {
@@ -33,12 +33,18 @@
         {
             return (BasicConstraints)obj;
         }
-        else if (obj instanceof ASN1Sequence)
+
+        if (obj instanceof ASN1Sequence)
         {
             return new BasicConstraints((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        if (obj instanceof X509Extension)
+        {
+            return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
+        }
+
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public BasicConstraints(
@@ -51,10 +57,25 @@
         }
         else
         {
-            this.cA = DERBoolean.getInstance(seq.getObjectAt(0));
+            if (seq.getObjectAt(0) instanceof DERBoolean)
+            {
+                this.cA = DERBoolean.getInstance(seq.getObjectAt(0));
+            }
+            else
+            {
+                this.cA = null;
+                this.pathLenConstraint = DERInteger.getInstance(seq.getObjectAt(0));
+            }
             if (seq.size() > 1)
             {
-                this.pathLenConstraint = DERInteger.getInstance(seq.getObjectAt(1));
+                if (this.cA != null)
+                {
+                    this.pathLenConstraint = DERInteger.getInstance(seq.getObjectAt(1));
+                }
+                else
+                {
+                    throw new IllegalArgumentException("wrong sequence in constructor");
+                }
             }
         }
     }
@@ -143,11 +164,11 @@
         if (cA != null)
         {
             v.add(cA);
-    
-            if (pathLenConstraint != null)
-            {
-                v.add(pathLenConstraint);
-            }
+        }
+
+        if (pathLenConstraint != null)  // yes some people actually do this when cA is false...
+        {
+            v.add(pathLenConstraint);
         }
 
         return new DERSequence(v);
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java b/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
index ed8ab58..67244b1 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
@@ -22,7 +22,7 @@
     public static CRLDistPoint getInstance(
         Object  obj)
     {
-        if (obj instanceof CRLDistPoint)
+        if (obj instanceof CRLDistPoint || obj == null)
         {
             return (CRLDistPoint)obj;
         }
@@ -31,7 +31,7 @@
             return new CRLDistPoint((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public CRLDistPoint(
@@ -80,4 +80,21 @@
     {
         return seq;
     }
+
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+        String       sep = System.getProperty("line.separator");
+
+        buf.append("CRLDistPoint:");
+        buf.append(sep);
+        DistributionPoint dp[] = getDistributionPoints();
+        for (int i = 0; i != dp.length; i++)
+        {
+            buf.append("    ");
+            buf.append(dp[i]);
+            buf.append(sep);
+        }
+        return buf.toString();
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java b/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
index e488086..5c74abf 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
@@ -1,9 +1,9 @@
 package org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.asn1.DERInteger;
 
+import java.math.BigInteger;
+
 /**
  * The CRLNumber object.
  * <pre>
@@ -24,4 +24,9 @@
     {
         return getPositiveValue();
     }
+
+    public String toString()
+    {
+        return "CRLNumber: " + getCRLNumber();
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java b/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
index 265c662..dfbccda 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
@@ -70,10 +70,18 @@
     public static final int superseded = 4;
     public static final int cessationOfOperation  = 5;
     public static final int certificateHold = 6;
+    // 7 -> unknown
     public static final int removeFromCRL = 8;
     public static final int privilegeWithdrawn = 9;
     public static final int aACompromise = 10;
-    
+
+    private static final String[] reasonString =
+    {
+        "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
+        "superseded", "cessationOfOperation", "certificateHold", "unknown",
+        "removeFromCRL", "privilegeWithdrawn", "aACompromise"
+    };
+
     public CRLReason(
         int reason)
     {
@@ -85,4 +93,19 @@
     {
         super(reason.getValue().intValue());
     }
+
+    public String toString()
+    {
+        String str;
+        int reason = getValue().intValue();
+        if (reason < 0 || reason > 10)
+        {
+            str = "invalid";
+        }
+        else
+        {
+            str = reasonString[reason];
+        }
+        return "CRLReason: " + str;
+    }    
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java b/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java
deleted file mode 100644
index 3e85dbd..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/CertPolicyId.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-
-/**
- * CertPolicyId, used in the CertificatePolicies and PolicyMappings
- * X509V3 Extensions.
- *
- * <pre>
- *     CertPolicyId ::= OBJECT IDENTIFIER
- * </pre>
- */
-public class CertPolicyId extends DERObjectIdentifier 
-{
-   public CertPolicyId (String id) 
-   {
-     super(id);
-   }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
index c7f890b..66c9630 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
@@ -9,6 +9,8 @@
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERSequence;
 
+import java.util.Enumeration;
+
 /**
  * PKIX RFC-2459
  *
@@ -48,7 +50,7 @@
             return new CertificateList((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public CertificateList(
@@ -76,6 +78,11 @@
         return tbsCertList.getRevokedCertificates();
     }
 
+    public Enumeration getRevokedCertificateEnumeration()
+    {
+        return tbsCertList.getRevokedCertificateEnumeration();
+    }
+
     public AlgorithmIdentifier getSignatureAlgorithm()
     {
         return sigAlgId;
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
deleted file mode 100644
index b59e08e..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
+++ /dev/null
@@ -1,147 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.util.Enumeration;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-public class CertificatePolicies
-    extends ASN1Encodable
-{
-    static final DERObjectIdentifier anyPolicy = new DERObjectIdentifier("2.5.29.32.0");
-
-    Vector policies = new Vector();
-
-/**
- * @deprecated use an ASN1Sequence of PolicyInformation
- */
-    public static CertificatePolicies getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-/**
- * @deprecated use an ASN1Sequence of PolicyInformation
- */
-    public static CertificatePolicies getInstance(
-        Object  obj)
-    {
-        if (obj instanceof CertificatePolicies)
-        {
-            return (CertificatePolicies)obj;
-        }
-        else if (obj instanceof ASN1Sequence)
-        {
-            return new CertificatePolicies((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("unknown object in factory");
-    }
-
-/**
- * @deprecated use an ASN1Sequence of PolicyInformation
- */
-    public CertificatePolicies(
-        ASN1Sequence   seq)
-    {
-        Enumeration e = seq.getObjects();
-        while (e.hasMoreElements())
-        {
-            ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
-            policies.addElement(s.getObjectAt(0));
-        }
-        // For now we just don't handle PolicyQualifiers
-    }
-
-    /**
-     * create a certificate policy with the given OID.
-     * @deprecated use an ASN1Sequence of PolicyInformation
-     */
-    public CertificatePolicies(
-        DERObjectIdentifier p)
-    {
-        policies.addElement(p);
-    }
-
-    /**
-     * create a certificate policy with the policy given by the OID represented
-     * by the string p.
-     * @deprecated use an ASN1Sequence of PolicyInformation
-     */
-    public CertificatePolicies(
-        String p)
-    {
-        this(new DERObjectIdentifier(p));
-    }
-
-    public void addPolicy(
-        String p)
-    {
-        policies.addElement(new DERObjectIdentifier(p));
-    }
-
-    public String getPolicy(int nr)
-    {
-        if (policies.size() > nr)
-        {
-            return ((DERObjectIdentifier)policies.elementAt(nr)).getId();
-        }
-        
-        return null;
-    }
-
-    /**
-     * <pre>
-     * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
-     *
-     * PolicyInformation ::= SEQUENCE {
-     *   policyIdentifier   CertPolicyId,
-     *   policyQualifiers   SEQUENCE SIZE (1..MAX) OF
-     *                           PolicyQualifierInfo OPTIONAL }
-     *
-     * CertPolicyId ::= OBJECT IDENTIFIER
-     *
-     * PolicyQualifierInfo ::= SEQUENCE {
-     *   policyQualifierId  PolicyQualifierId,
-     *   qualifier          ANY DEFINED BY policyQualifierId }
-     *
-     * PolicyQualifierId ::=
-     *   OBJECT IDENTIFIER (id-qt-cps | id-qt-unotice)
-     * </pre>
-     * @deprecated use an ASN1Sequence of PolicyInformation
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        // We only do policyIdentifier yet...
-        for (int i=0;i<policies.size();i++)
-        {
-            v.add(new DERSequence((DERObjectIdentifier)policies.elementAt(i)));
-        }
-
-        return new DERSequence(v);
-    }
-
-    public String toString()
-    {
-        String p = null;
-        for (int i=0;i<policies.size();i++)
-        {
-            if (p != null)
-            {
-                p += ", ";
-            }
-            p += ((DERObjectIdentifier)policies.elementAt(i)).getId();
-        }
-        return "CertificatePolicies: "+p;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java b/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
index 882e71e..baf93e1 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
@@ -44,7 +44,7 @@
             return new DigestInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public DigestInfo(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java b/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java
deleted file mode 100644
index 90b025e..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/DisplayText.java
+++ /dev/null
@@ -1,165 +0,0 @@
-
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Choice;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERBMPString;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERUTF8String;
-import org.bouncycastle.asn1.DERVisibleString;
-import org.bouncycastle.asn1.DERString;
-
-/**
- * <code>DisplayText</code> class, used in
- * <code>CertificatePolicies</code> X509 V3 extensions (in policy qualifiers).
- *
- * <p>It stores a string in a chosen encoding. 
- * <pre>
- * DisplayText ::= CHOICE {
- *      ia5String        IA5String      (SIZE (1..200)),
- *      visibleString    VisibleString  (SIZE (1..200)),
- *      bmpString        BMPString      (SIZE (1..200)),
- *      utf8String       UTF8String     (SIZE (1..200)) }
- * </pre>
- * @see PolicyQualifierInfo
- * @see PolicyInformation
- */
-public class DisplayText 
-    extends ASN1Encodable
-    implements ASN1Choice
-{
-   /**
-    * Constant corresponding to ia5String encoding. 
-    *
-    */
-   public static final int CONTENT_TYPE_IA5STRING = 0;
-   /**
-    * Constant corresponding to bmpString encoding. 
-    *
-    */
-   public static final int CONTENT_TYPE_BMPSTRING = 1;
-   /**
-    * Constant corresponding to utf8String encoding. 
-    *
-    */
-   public static final int CONTENT_TYPE_UTF8STRING = 2;
-   /**
-    * Constant corresponding to visibleString encoding. 
-    *
-    */
-   public static final int CONTENT_TYPE_VISIBLESTRING = 3;
-
-   /**
-    * Describe constant <code>DISPLAY_TEXT_MAXIMUM_SIZE</code> here.
-    *
-    */
-   public static final int DISPLAY_TEXT_MAXIMUM_SIZE = 200;
-   
-   int contentType;
-   DERString contents;
-   
-   /**
-    * Creates a new <code>DisplayText</code> instance.
-    *
-    * @param type the desired encoding type for the text. 
-    * @param text the text to store. Strings longer than 200
-    * characters are truncated. 
-    */
-   public DisplayText (int type, String text) 
-   {
-      if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
-      {
-         // RFC3280 limits these strings to 200 chars
-         // truncate the string
-         text = text.substring (0, DISPLAY_TEXT_MAXIMUM_SIZE);
-      }
-     
-      contentType = type;
-      switch (type)
-      {
-         case CONTENT_TYPE_IA5STRING:
-            contents = (DERString)new DERIA5String (text);
-            break;
-         case CONTENT_TYPE_UTF8STRING:
-            contents = (DERString)new DERUTF8String(text);
-            break;
-         case CONTENT_TYPE_VISIBLESTRING:
-            contents = (DERString)new DERVisibleString(text);
-            break;
-         case CONTENT_TYPE_BMPSTRING:
-            contents = (DERString)new DERBMPString(text);
-            break;
-         default:
-            contents = (DERString)new DERUTF8String(text);
-            break;
-      }
-   }
-   
-   /**
-    * Creates a new <code>DisplayText</code> instance.
-    *
-    * @param text the text to encapsulate. Strings longer than 200
-    * characters are truncated. 
-    */
-   public DisplayText (String text) 
-   {
-      // by default use UTF8String
-      if (text.length() > DISPLAY_TEXT_MAXIMUM_SIZE)
-      {
-         text = text.substring(0, DISPLAY_TEXT_MAXIMUM_SIZE);
-      }
-      
-      contentType = CONTENT_TYPE_UTF8STRING;
-      contents = new DERUTF8String(text);
-   }
-
-   /**
-    * Creates a new <code>DisplayText</code> instance.
-    * <p>Useful when reading back a <code>DisplayText</code> class
-    * from it's ASN1Encodable/DEREncodable form. 
-    *
-    * @param de a <code>DEREncodable</code> instance. 
-    */
-   public DisplayText(DERString de)
-   {
-      contents = de;
-   }
-
-   public static DisplayText getInstance(Object de) 
-   {
-      if (de instanceof DERString)
-      {
-          return new DisplayText((DERString)de);
-      }
-      else if (de instanceof DisplayText)
-      {
-          return (DisplayText)de;
-      }
-
-      throw new IllegalArgumentException("illegal object in getInstance");
-   }
-
-   public static DisplayText getInstance(
-       ASN1TaggedObject obj,
-       boolean          explicit)
-   {
-       return getInstance(obj.getObject()); // must be explicitly tagged
-   }
-   
-   public DERObject toASN1Object() 
-   {
-      return (DERObject)contents;
-   }
-
-   /**
-    * Returns the stored <code>String</code> object. 
-    *
-    * @return the stored text as a <code>String</code>. 
-    */
-   public String getString() 
-   {
-      return contents.getString();
-   }   
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java b/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
index ea5aa0b..e57c408 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
@@ -118,4 +118,41 @@
 
         return new DERSequence(v);
     }
+
+    public String toString()
+    {
+        String       sep = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+        buf.append("DistributionPoint: [");
+        buf.append(sep);
+        if (distributionPoint != null)
+        {
+            appendObject(buf, sep, "distributionPoint", distributionPoint.toString());
+        }
+        if (reasons != null)
+        {
+            appendObject(buf, sep, "reasons", reasons.toString());
+        }
+        if (cRLIssuer != null)
+        {
+            appendObject(buf, sep, "cRLIssuer", cRLIssuer.toString());
+        }
+        buf.append("]");
+        buf.append(sep);
+        return buf.toString();
+    }
+
+    private void appendObject(StringBuffer buf, String sep, String name, String value)
+    {
+        String       indent = "    ";
+
+        buf.append(indent);
+        buf.append(name);
+        buf.append(":");
+        buf.append(sep);
+        buf.append(indent);
+        buf.append(indent);
+        buf.append(value);
+        buf.append(sep);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java b/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
index 91ef110..a59f105 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
@@ -46,7 +46,7 @@
             return new DistributionPointName((ASN1TaggedObject)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     /*
@@ -67,7 +67,13 @@
         this.type = type;
         this.name = name;
     }
-    
+
+    public DistributionPointName(
+        GeneralNames name)
+    {
+        this(FULL_NAME, name);
+    }
+
     /**
      * Return the tag number applying to the underlying choice.
      * 
@@ -107,4 +113,37 @@
     {
         return new DERTaggedObject(false, type, name);
     }
+
+    public String toString()
+    {
+        String       sep = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+        buf.append("DistributionPointName: [");
+        buf.append(sep);
+        if (type == FULL_NAME)
+        {
+            appendObject(buf, sep, "fullName", name.toString());
+        }
+        else
+        {
+            appendObject(buf, sep, "nameRelativeToCRLIssuer", name.toString());
+        }
+        buf.append("]");
+        buf.append(sep);
+        return buf.toString();
+    }
+
+    private void appendObject(StringBuffer buf, String sep, String name, String value)
+    {
+        String       indent = "    ";
+
+        buf.append(indent);
+        buf.append(name);
+        buf.append(":");
+        buf.append(sep);
+        buf.append(indent);
+        buf.append(indent);
+        buf.append(value);
+        buf.append(sep);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
deleted file mode 100644
index a84b97f..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The extendedKeyUsage object.
- * <pre>
- *      extendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
- * </pre>
- */
-public class ExtendedKeyUsage
-    extends ASN1Encodable
-{
-    Hashtable     usageTable = new Hashtable();
-    ASN1Sequence  seq;
-
-    public static ExtendedKeyUsage getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        return getInstance(ASN1Sequence.getInstance(obj, explicit));
-    }
-
-    public static ExtendedKeyUsage getInstance(
-        Object obj)
-    {
-        if(obj == null || obj instanceof ExtendedKeyUsage) 
-        {
-            return (ExtendedKeyUsage)obj;
-        }
-        
-        if(obj instanceof ASN1Sequence) 
-        {
-            return new ExtendedKeyUsage((ASN1Sequence)obj);
-        }
-        
-        throw new IllegalArgumentException("Invalid ExtendedKeyUsage: " + obj.getClass().getName());
-    }
-
-    public ExtendedKeyUsage(
-        KeyPurposeId  usage)
-    {
-        this.seq = new DERSequence(usage);
-
-        this.usageTable.put(usage, usage);
-    }
-    
-    public ExtendedKeyUsage(
-        ASN1Sequence  seq)
-    {
-        this.seq = seq;
-
-        Enumeration e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            Object  o = e.nextElement();
-
-            this.usageTable.put(o, o);
-        }
-    }
-
-    public ExtendedKeyUsage(
-        Vector  usages)
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        Enumeration         e = usages.elements();
-
-        while (e.hasMoreElements())
-        {
-            DERObject  o = (DERObject)e.nextElement();
-
-            v.add(o);
-            this.usageTable.put(o, o);
-        }
-
-        this.seq = new DERSequence(v);
-    }
-
-    public boolean hasKeyPurposeId(
-        KeyPurposeId keyPurposeId)
-    {
-        return (usageTable.get(keyPurposeId) != null);
-    }
-
-    public int size()
-    {
-        return usageTable.size();
-    }
-    
-    public DERObject toASN1Object()
-    {
-        return seq;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java b/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
index e015cee..c657c7b 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.asn1.x509;
 
+import java.util.StringTokenizer;
+
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -9,7 +11,9 @@
 import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.util.IPAddress;
 
 /**
  * The GeneralName object.
@@ -106,25 +110,58 @@
     }
     
     /**
-     * Create a General name for the given tag from the passed in String.
-     * 
+     * Create a GeneralName for the given tag from the passed in String.
+     * <p>
+     * This constructor can handle:
+     * <ul>
+     * <li>rfc822Name
+     * <li>iPAddress
+     * <li>directoryName
+     * <li>dNSName
+     * <li>uniformResourceIdentifier
+     * <li>registeredID
+     * </ul>
+     * For x400Address, otherName and ediPartyName there is no common string
+     * format defined.
+     * <p>
+     * Note: A directory name can be encoded in different ways into a byte
+     * representation. Be aware of this if the byte representation is used for
+     * comparing results.
+     *
      * @param tag tag number
      * @param name string representation of name
+     * @throws IllegalArgumentException if the string encoding is not correct or     *             not supported.
      */
     public GeneralName(
         int       tag,
         String    name)
     {
+        this.tag = tag;
+
         if (tag == rfc822Name || tag == dNSName || tag == uniformResourceIdentifier)
         {
-            this.tag = tag;
             this.obj = new DERIA5String(name);
         }
         else if (tag == registeredID)
         {
-            this.tag = tag;
             this.obj = new DERObjectIdentifier(name);
         }
+        else if (tag == directoryName)
+        {
+            this.obj = new X509Name(name);
+        }
+        else if (tag == iPAddress)
+        {
+            byte[] enc = toGeneralNameEncoding(name);
+            if (enc != null)
+            {
+                this.obj = new DEROctetString(enc);
+            }
+            else
+            {
+                throw new IllegalArgumentException("IP Address is invalid");
+            }
+        }
         else
         {
             throw new IllegalArgumentException("can't process String for tag: " + tag);
@@ -147,27 +184,27 @@
             switch (tag)
             {
             case otherName:
-                return new GeneralName(ASN1Sequence.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
             case rfc822Name:
-                return new GeneralName(DERIA5String.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
             case dNSName:
-                return new GeneralName(DERIA5String.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
             case x400Address:
                 throw new IllegalArgumentException("unknown tag: " + tag);
             case directoryName:
-                return new GeneralName(ASN1Sequence.getInstance(tagObj, true), tag);
+                return new GeneralName(tag, X509Name.getInstance(tagObj, true));
             case ediPartyName:
-                return new GeneralName(ASN1Sequence.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
             case uniformResourceIdentifier:
-                return new GeneralName(DERIA5String.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
             case iPAddress:
-                return new GeneralName(ASN1OctetString.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, ASN1OctetString.getInstance(tagObj, false));
             case registeredID:
-                return new GeneralName(DERObjectIdentifier.getInstance(tagObj, false), tag);
+                return new GeneralName(tag, DERObjectIdentifier.getInstance(tagObj, false));
             }
         }
 
-        throw new IllegalArgumentException("unknown object in getInstance");
+        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
     public static GeneralName getInstance(
@@ -187,6 +224,192 @@
         return obj;
     }
 
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append(tag);
+        buf.append(": ");
+        switch (tag)
+        {
+        case rfc822Name:
+        case dNSName:
+        case uniformResourceIdentifier:
+            buf.append(DERIA5String.getInstance(obj).getString());
+            break;
+        case directoryName:
+            buf.append(X509Name.getInstance(obj).toString());
+            break;
+        default:
+            buf.append(obj.toString());
+        }
+        return buf.toString();
+    }
+
+    private byte[] toGeneralNameEncoding(String ip)
+    {
+        if (IPAddress.isValidIPv6WithNetmask(ip) || IPAddress.isValidIPv6(ip))
+        {
+            int    slashIndex = ip.indexOf('/');
+
+            if (slashIndex < 0)
+            {
+                byte[] addr = new byte[16];
+                int[]  parsedIp = parseIPv6(ip);
+                copyInts(parsedIp, addr, 0);
+
+                return addr;
+            }
+            else
+            {
+                byte[] addr = new byte[32];
+                int[]  parsedIp = parseIPv6(ip.substring(0, slashIndex));
+                copyInts(parsedIp, addr, 0);
+                String mask = ip.substring(slashIndex + 1);
+                if (mask.indexOf(':') > 0)
+                {
+                    parsedIp = parseIPv6(mask);
+                }
+                else
+                {
+                    parsedIp = parseMask(mask);
+                }
+                copyInts(parsedIp, addr, 16);
+
+                return addr;
+            }
+        }
+        else if (IPAddress.isValidIPv4WithNetmask(ip) || IPAddress.isValidIPv4(ip))
+        {
+            int    slashIndex = ip.indexOf('/');
+
+            if (slashIndex < 0)
+            {
+                byte[] addr = new byte[4];
+
+                parseIPv4(ip, addr, 0);
+
+                return addr;
+            }
+            else
+            {
+                byte[] addr = new byte[8];
+
+                parseIPv4(ip.substring(0, slashIndex), addr, 0);
+
+                String mask = ip.substring(slashIndex + 1);
+                if (mask.indexOf('.') > 0)
+                {
+                    parseIPv4(mask, addr, 4);
+                }
+                else
+                {
+                    parseIPv4Mask(mask, addr, 4);
+                }
+
+                return addr;
+            }
+        }
+
+        return null;
+    }
+
+    private void parseIPv4Mask(String mask, byte[] addr, int offset)
+    {
+        int   maskVal = Integer.parseInt(mask);
+
+        for (int i = 0; i != maskVal; i++)
+        {
+            addr[(i / 8) + offset] |= 1 << (i % 8);
+        }
+    }
+
+    private void parseIPv4(String ip, byte[] addr, int offset)
+    {
+        StringTokenizer sTok = new StringTokenizer(ip, "./");
+        int    index = 0;
+
+        while (sTok.hasMoreTokens())
+        {
+            addr[offset + index++] = (byte)Integer.parseInt(sTok.nextToken());
+        }
+    }
+
+    private int[] parseMask(String mask)
+    {
+        int[] res = new int[8];
+        int   maskVal = Integer.parseInt(mask);
+
+        for (int i = 0; i != maskVal; i++)
+        {
+            res[i / 16] |= 1 << (i % 16);
+        }
+        return res;
+    }
+
+    private void copyInts(int[] parsedIp, byte[] addr, int offSet)
+    {
+        for (int i = 0; i != parsedIp.length; i++)
+        {
+            addr[(i * 2) + offSet] = (byte)(parsedIp[i] >> 8);
+            addr[(i * 2 + 1) + offSet] = (byte)parsedIp[i];
+        }
+    }
+
+    private int[] parseIPv6(String ip)
+    {
+        StringTokenizer sTok = new StringTokenizer(ip, ":", true);
+        int index = 0;
+        int[] val = new int[8];
+
+        if (ip.charAt(0) == ':' && ip.charAt(1) == ':')
+        {
+           sTok.nextToken(); // skip the first one
+        }
+
+        int doubleColon = -1;
+
+        while (sTok.hasMoreTokens())
+        {
+            String e = sTok.nextToken();
+
+            if (e.equals(":"))
+            {
+                doubleColon = index;
+                val[index++] = 0;
+            }
+            else
+            {
+                if (e.indexOf('.') < 0)
+                {
+                    val[index++] = Integer.parseInt(e, 16);
+                    if (sTok.hasMoreTokens())
+                    {
+                        sTok.nextToken();
+                    }
+                }
+                else
+                {
+                    StringTokenizer eTok = new StringTokenizer(e, ".");
+
+                    val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
+                    val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
+                }
+            }
+        }
+
+        if (index != val.length)
+        {
+            System.arraycopy(val, doubleColon, val, val.length - (index - doubleColon), index - doubleColon);
+            for (int i = doubleColon; i != val.length - (index - doubleColon); i++)
+            {
+                val[i] = 0;
+            }
+        }
+
+        return val;
+    }
+
     public DERObject toASN1Object()
     {
         if (tag == directoryName)       // directoryName is explicitly tagged as it is a CHOICE
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java b/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
index d2f8d7d..f35e10d 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
@@ -9,7 +9,7 @@
 public class GeneralNames
     extends ASN1Encodable
 {
-    ASN1Sequence            seq;
+    private final GeneralName[] names;
 
     public static GeneralNames getInstance(
         Object  obj)
@@ -42,25 +42,27 @@
     public GeneralNames(
         GeneralName  name)
     {
-        this.seq = new DERSequence(name);
+        this.names = new GeneralName[] { name };
     }
     
     public GeneralNames(
         ASN1Sequence  seq)
     {
-        this.seq = seq;
-    }
+        this.names = new GeneralName[seq.size()];
 
-    public GeneralName[] getNames()
-    {
-        GeneralName[]   names = new GeneralName[seq.size()];
-        
         for (int i = 0; i != seq.size(); i++)
         {
             names[i] = GeneralName.getInstance(seq.getObjectAt(i));
         }
-        
-        return names;
+    }
+
+    public GeneralName[] getNames()
+    {
+        GeneralName[] tmp = new GeneralName[names.length];
+
+        System.arraycopy(names, 0, tmp, 0, names.length);
+
+        return tmp;
     }
 
     /**
@@ -71,6 +73,23 @@
      */
     public DERObject toASN1Object()
     {
-        return seq;
+        return new DERSequence(names);
+    }
+
+    public String toString()
+    {
+        StringBuffer  buf = new StringBuffer();
+        String        sep = System.getProperty("line.separator");
+
+        buf.append("GeneralNames:");
+        buf.append(sep);
+
+        for (int i = 0; i != names.length; i++)
+        {
+            buf.append("    ");
+            buf.append(names[i]);
+            buf.append(sep);
+        }
+        return buf.toString();
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java b/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
index e3e4240..326ee20 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Sequence;
@@ -11,6 +9,8 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+import java.math.BigInteger;
+
 /**
  * Class for containing a restriction object subtrees in NameConstraints. See
  * RFC 3280.
@@ -110,6 +110,11 @@
         }
     }
 
+    public GeneralSubtree(GeneralName base)
+    {
+        this(base, null, null);
+    }
+
     public static GeneralSubtree getInstance(
         ASN1TaggedObject o,
         boolean explicit)
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/src/main/java/org/bouncycastle/asn1/x509/Holder.java
index 8b9a08c..2425847 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/Holder.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/Holder.java
@@ -10,28 +10,45 @@
 
 /**
  * The Holder object.
+ * <p>
+ * For an v2 attribute certificate this is:
+ * 
  * <pre>
- *  Holder ::= SEQUENCE {
- *        baseCertificateID   [0] IssuerSerial OPTIONAL,
- *                 -- the issuer and serial number of
- *                 -- the holder's Public Key Certificate
- *        entityName          [1] GeneralNames OPTIONAL,
- *                 -- the name of the claimant or role
- *        objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
- *                 -- used to directly authenticate the holder,
- *                 -- for example, an executable
- *  }
+ *            Holder ::= SEQUENCE {
+ *                  baseCertificateID   [0] IssuerSerial OPTIONAL,
+ *                           -- the issuer and serial number of
+ *                           -- the holder's Public Key Certificate
+ *                  entityName          [1] GeneralNames OPTIONAL,
+ *                           -- the name of the claimant or role
+ *                  objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
+ *                           -- used to directly authenticate the holder,
+ *                           -- for example, an executable
+ *            }
+ * </pre>
+ * 
+ * <p>
+ * For an v1 attribute certificate this is:
+ * 
+ * <pre>
+ *         subject CHOICE {
+ *          baseCertificateID [0] IssuerSerial,
+ *          -- associated with a Public Key Certificate
+ *          subjectName [1] GeneralNames },
+ *          -- associated with a name
  * </pre>
  */
 public class Holder
     extends ASN1Encodable
 {
-    IssuerSerial        baseCertificateID;
-    GeneralNames        entityName;
-    ObjectDigestInfo    objectDigestInfo;
+    IssuerSerial baseCertificateID;
 
-    public static Holder getInstance(
-        Object  obj)
+    GeneralNames entityName;
+
+    ObjectDigestInfo objectDigestInfo;
+
+    private int version = 1;
+
+    public static Holder getInstance(Object obj)
     {
         if (obj instanceof Holder)
         {
@@ -41,23 +58,53 @@
         {
             return new Holder((ASN1Sequence)obj);
         }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            return new Holder((ASN1TaggedObject)obj);
+        }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
-    public Holder(
-        ASN1Sequence    seq)
+    /**
+     * Constructor for a holder for an v1 attribute certificate.
+     * 
+     * @param tagObj The ASN.1 tagged holder object.
+     */
+    public Holder(ASN1TaggedObject tagObj)
+    {
+        switch (tagObj.getTagNo())
+        {
+        case 0:
+            baseCertificateID = IssuerSerial.getInstance(tagObj, false);
+            break;
+        case 1:
+            entityName = GeneralNames.getInstance(tagObj, false);
+            break;
+        default:
+            throw new IllegalArgumentException("unknown tag in Holder");
+        }
+        version = 0;
+    }
+
+    /**
+     * Constructor for a holder for an v2 attribute certificate. *
+     * 
+     * @param seq The ASN.1 sequence.
+     */
+    public Holder(ASN1Sequence seq)
     {
         if (seq.size() > 3)
         {
             throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
+                + seq.size());
         }
 
         for (int i = 0; i != seq.size(); i++)
         {
-            ASN1TaggedObject    tObj = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
-            
+            ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(seq
+                .getObjectAt(i));
+
             switch (tObj.getTagNo())
             {
             case 0:
@@ -73,54 +120,123 @@
                 throw new IllegalArgumentException("unknown tag in Holder");
             }
         }
+        version = 1;
     }
-    
-    public Holder(
-        IssuerSerial    baseCertificateID)
+
+    public Holder(IssuerSerial baseCertificateID)
     {
         this.baseCertificateID = baseCertificateID;
     }
+
+    /**
+     * Constructs a holder from a IssuerSerial.
+     * @param baseCertificateID The IssuerSerial.
+     * @param version The version of the attribute certificate. 
+     */
+    public Holder(IssuerSerial baseCertificateID, int version)
+    {
+        this.baseCertificateID = baseCertificateID;
+        this.version = version;
+    }
     
-    public Holder(
-        GeneralNames    entityName)
+    /**
+     * Returns 1 for v2 attribute certificates or 0 for v1 attribute
+     * certificates. 
+     * @return The version of the attribute certificate.
+     */
+    public int getVersion()
+    {
+        return version;
+    }
+
+    /**
+     * Constructs a holder with an entityName for v2 attribute certificates or
+     * with a subjectName for v1 attribute certificates.
+     * 
+     * @param entityName The entity or subject name.
+     */
+    public Holder(GeneralNames entityName)
     {
         this.entityName = entityName;
     }
+
+    /**
+     * Constructs a holder with an entityName for v2 attribute certificates or
+     * with a subjectName for v1 attribute certificates.
+     * 
+     * @param entityName The entity or subject name.
+     * @param version The version of the attribute certificate. 
+     */
+    public Holder(GeneralNames entityName, int version)
+    {
+        this.entityName = entityName;
+        this.version = version;
+    }
     
+    /**
+     * Constructs a holder from an object digest info.
+     * 
+     * @param objectDigestInfo The object digest info object.
+     */
+    public Holder(ObjectDigestInfo objectDigestInfo)
+    {
+        this.objectDigestInfo = objectDigestInfo;
+    }
+
     public IssuerSerial getBaseCertificateID()
     {
         return baseCertificateID;
     }
-    
+
+    /**
+     * Returns the entityName for an v2 attribute certificate or the subjectName
+     * for an v1 attribute certificate.
+     * 
+     * @return The entityname or subjectname.
+     */
     public GeneralNames getEntityName()
     {
         return entityName;
     }
-    
+
     public ObjectDigestInfo getObjectDigestInfo()
     {
         return objectDigestInfo;
     }
-    
+
     public DERObject toASN1Object()
     {
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        if (baseCertificateID != null)
+        if (version == 1)
         {
-            v.add(new DERTaggedObject(false, 0, baseCertificateID));
-        }
+            ASN1EncodableVector v = new ASN1EncodableVector();
 
-        if (entityName != null)
+            if (baseCertificateID != null)
+            {
+                v.add(new DERTaggedObject(false, 0, baseCertificateID));
+            }
+
+            if (entityName != null)
+            {
+                v.add(new DERTaggedObject(false, 1, entityName));
+            }
+
+            if (objectDigestInfo != null)
+            {
+                v.add(new DERTaggedObject(false, 2, objectDigestInfo));
+            }
+
+            return new DERSequence(v);
+        }
+        else
         {
-            v.add(new DERTaggedObject(false, 1, entityName));
+            if (entityName != null)
+            {
+                return new DERTaggedObject(false, 1, entityName);
+            }
+            else
+            {
+                return new DERTaggedObject(false, 0, baseCertificateID);
+            }
         }
-
-        if (objectDigestInfo != null)
-        {
-            v.add(new DERTaggedObject(false, 2, objectDigestInfo));
-        }
-
-        return new DERSequence(v);
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java b/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java
deleted file mode 100644
index 07e07cf..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/IetfAttrSyntax.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.util.Enumeration;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.DERUTF8String;
-
-/**
- * Implementation of <code>IetfAttrSyntax</code> as specified by RFC3281.
- */
-public class IetfAttrSyntax
-    extends ASN1Encodable
-{
-    public static final int VALUE_OCTETS    = 1;
-    public static final int VALUE_OID       = 2;
-    public static final int VALUE_UTF8      = 3;
-    GeneralNames            policyAuthority = null;
-    Vector                  values          = new Vector();
-    int                     valueChoice     = -1;
-
-    /**
-     *  
-     */
-    public IetfAttrSyntax(ASN1Sequence seq)
-    {
-        int i = 0;
-
-        if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
-        {
-            policyAuthority = GeneralNames.getInstance(((ASN1TaggedObject)seq.getObjectAt(0)), false);
-            i++;
-        }
-        else if (seq.size() == 2)
-        { // VOMS fix
-            policyAuthority = GeneralNames.getInstance(seq.getObjectAt(0));
-            i++;
-        }
-
-        if (!(seq.getObjectAt(i) instanceof ASN1Sequence))
-        {
-            throw new IllegalArgumentException("Non-IetfAttrSyntax encoding");
-        }
-
-        seq = (ASN1Sequence)seq.getObjectAt(i);
-
-        for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
-        {
-            DERObject obj = (DERObject)e.nextElement();
-            int type;
-
-            if (obj instanceof DERObjectIdentifier)
-            {
-                type = VALUE_OID;
-            }
-            else if (obj instanceof DERUTF8String)
-            {
-                type = VALUE_UTF8;
-            }
-            else if (obj instanceof DEROctetString)
-            {
-                type = VALUE_OCTETS;
-            }
-            else
-            {
-                throw new IllegalArgumentException("Bad value type encoding IetfAttrSyntax");
-            }
-
-            if (valueChoice < 0)
-            {
-                valueChoice = type;
-            }
-
-            if (type != valueChoice)
-            {
-                throw new IllegalArgumentException("Mix of value types in IetfAttrSyntax");
-            }
-
-            values.addElement(obj);
-        }
-    }
-
-    public GeneralNames getPolicyAuthority()
-    {
-        return policyAuthority;
-    }
-
-    public int getValueType()
-    {
-        return valueChoice;
-    }
-
-    public Object[] getValues()
-    {
-        if (this.getValueType() == VALUE_OCTETS)
-        {
-            ASN1OctetString[] tmp = new ASN1OctetString[values.size()];
-            
-            for (int i = 0; i != tmp.length; i++)
-            {
-                tmp[i] = (ASN1OctetString)values.elementAt(i);
-            }
-            
-            return tmp;
-        }
-        else if (this.getValueType() == VALUE_OID)
-        {
-            DERObjectIdentifier[] tmp = new DERObjectIdentifier[values.size()];
-            
-            for (int i = 0; i != tmp.length; i++)
-            {
-                tmp[i] = (DERObjectIdentifier)values.elementAt(i);
-            }
-            
-            return tmp;
-        }
-        else
-        {
-            DERUTF8String[] tmp = new DERUTF8String[values.size()];
-            
-            for (int i = 0; i != tmp.length; i++)
-            {
-                tmp[i] = (DERUTF8String)values.elementAt(i);
-            }
-            
-            return tmp;
-        }
-    }
-
-    /**
-     * 
-     * <pre>
-     * 
-     *  IetfAttrSyntax ::= SEQUENCE {
-     *    policyAuthority [0] GeneralNames OPTIONAL,
-     *    values SEQUENCE OF CHOICE {
-     *      octets OCTET STRING,
-     *      oid OBJECT IDENTIFIER,
-     *      string UTF8String
-     *    }
-     *  }
-     *  
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        if (policyAuthority != null)
-        {
-            v.add(new DERTaggedObject(0, policyAuthority));
-        }
-
-        ASN1EncodableVector v2 = new ASN1EncodableVector();
-
-        for (Enumeration i = values.elements(); i.hasMoreElements();)
-        {
-            v2.add((ASN1Encodable)i.nextElement());
-        }
-
-        v.add(new DERSequence(v2));
-
-        return new DERSequence(v);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
index 07e468f..8900531 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
@@ -1,39 +1,51 @@
 package org.bouncycastle.asn1.x509;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERBoolean;
 import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
 
 /**
- * IssuingDistributionPoint ::= SEQUENCE {
- *      distributionPoint          [0] DistributionPointName OPTIONAL,
- *      onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
- *      onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
- *      onlySomeReasons            [3] ReasonFlags OPTIONAL,
- *      indirectCRL                [4] BOOLEAN DEFAULT FALSE,
- *      onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+ * <pre>
+ * IssuingDistributionPoint ::= SEQUENCE { 
+ *   distributionPoint          [0] DistributionPointName OPTIONAL, 
+ *   onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE, 
+ *   onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE, 
+ *   onlySomeReasons            [3] ReasonFlags OPTIONAL, 
+ *   indirectCRL                [4] BOOLEAN DEFAULT FALSE,
+ *   onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+ * </pre>
  */
 public class IssuingDistributionPoint
     extends ASN1Encodable
 {
-    private boolean         onlyContainsUserCerts;
-    private boolean         onlyContainsCACerts;
-    private boolean         indirectCRL;
-    private boolean         onlyContainsAttributeCerts;
+    private DistributionPointName distributionPoint;
 
-    private ASN1Sequence    seq;
+    private boolean onlyContainsUserCerts;
+
+    private boolean onlyContainsCACerts;
+
+    private ReasonFlags onlySomeReasons;
+
+    private boolean indirectCRL;
+
+    private boolean onlyContainsAttributeCerts;
+
+    private ASN1Sequence seq;
 
     public static IssuingDistributionPoint getInstance(
         ASN1TaggedObject obj,
-        boolean          explicit)
+        boolean explicit)
     {
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
 
     public static IssuingDistributionPoint getInstance(
-        Object  obj)
+        Object obj)
     {
         if (obj == null || obj instanceof IssuingDistributionPoint)
         {
@@ -44,24 +56,93 @@
             return new IssuingDistributionPoint((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+    }
+
+    /**
+     * Constructor from given details.
+     * 
+     * @param distributionPoint
+     *            May contain an URI as pointer to most current CRL.
+     * @param onlyContainsUserCerts Covers revocation information for end certificates.
+     * @param onlyContainsCACerts Covers revocation information for CA certificates.
+     * 
+     * @param onlySomeReasons
+     *            Which revocation reasons does this point cover.
+     * @param indirectCRL
+     *            If <code>true</code> then the CRL contains revocation
+     *            information about certificates ssued by other CAs.
+     * @param onlyContainsAttributeCerts Covers revocation information for attribute certificates.
+     */
+    public IssuingDistributionPoint(
+        DistributionPointName distributionPoint,
+        boolean onlyContainsUserCerts,
+        boolean onlyContainsCACerts,
+        ReasonFlags onlySomeReasons,
+        boolean indirectCRL,
+        boolean onlyContainsAttributeCerts)
+    {
+        this.distributionPoint = distributionPoint;
+        this.indirectCRL = indirectCRL;
+        this.onlyContainsAttributeCerts = onlyContainsAttributeCerts;
+        this.onlyContainsCACerts = onlyContainsCACerts;
+        this.onlyContainsUserCerts = onlyContainsUserCerts;
+        this.onlySomeReasons = onlySomeReasons;
+
+        ASN1EncodableVector vec = new ASN1EncodableVector();
+        if (distributionPoint != null)
+        {                                    // CHOICE item so explicitly tagged
+            vec.add(new DERTaggedObject(true, 0, distributionPoint));
+        }
+        if (onlyContainsUserCerts)
+        {
+            // BEGIN android-changed
+            vec.add(new DERTaggedObject(false, 1, DERBoolean.TRUE));
+            // END android-changed
+        }
+        if (onlyContainsCACerts)
+        {
+            // BEGIN android-changed
+            vec.add(new DERTaggedObject(false, 2, DERBoolean.TRUE));
+            // END android-changed
+        }
+        if (onlySomeReasons != null)
+        {
+            vec.add(new DERTaggedObject(false, 3, onlySomeReasons));
+        }
+        if (indirectCRL)
+        {
+            // BEGIN android-changed
+            vec.add(new DERTaggedObject(false, 4, DERBoolean.TRUE));
+            // END android-changed
+        }
+        if (onlyContainsAttributeCerts)
+        {
+            // BEGIN android-changed
+            vec.add(new DERTaggedObject(false, 5, DERBoolean.TRUE));
+            // END android-changed
+        }
+
+        seq = new DERSequence(vec);
     }
 
     /**
      * Constructor from ASN1Sequence
      */
     public IssuingDistributionPoint(
-        ASN1Sequence  seq)
+        ASN1Sequence seq)
     {
         this.seq = seq;
 
         for (int i = 0; i != seq.size(); i++)
         {
-            ASN1TaggedObject  o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+            ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
 
             switch (o.getTagNo())
             {
             case 0:
+                                                    // CHOICE so explicit
+                distributionPoint = DistributionPointName.getInstance(o, true);
                 break;
             case 1:
                 onlyContainsUserCerts = DERBoolean.getInstance(o, false).isTrue();
@@ -70,6 +151,7 @@
                 onlyContainsCACerts = DERBoolean.getInstance(o, false).isTrue();
                 break;
             case 3:
+                onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
                 break;
             case 4:
                 indirectCRL = DERBoolean.getInstance(o, false).isTrue();
@@ -78,7 +160,8 @@
                 onlyContainsAttributeCerts = DERBoolean.getInstance(o, false).isTrue();
                 break;
             default:
-                throw new IllegalArgumentException("unknown tag in IssuingDistributionPoint");
+                throw new IllegalArgumentException(
+                        "unknown tag in IssuingDistributionPoint");
             }
         }
     }
@@ -103,8 +186,79 @@
         return onlyContainsAttributeCerts;
     }
 
+    /**
+     * @return Returns the distributionPoint.
+     */
+    public DistributionPointName getDistributionPoint()
+    {
+        return distributionPoint;
+    }
+
+    /**
+     * @return Returns the onlySomeReasons.
+     */
+    public ReasonFlags getOnlySomeReasons()
+    {
+        return onlySomeReasons;
+    }
+
     public DERObject toASN1Object()
     {
         return seq;
     }
+
+    public String toString()
+    {
+        String       sep = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+
+        buf.append("IssuingDistributionPoint: [");
+        buf.append(sep);
+        if (distributionPoint != null)
+        {
+            appendObject(buf, sep, "distributionPoint", distributionPoint.toString());
+        }
+        if (onlyContainsUserCerts)
+        {
+            appendObject(buf, sep, "onlyContainsUserCerts", booleanToString(onlyContainsUserCerts));
+        }
+        if (onlyContainsCACerts)
+        {
+            appendObject(buf, sep, "onlyContainsCACerts", booleanToString(onlyContainsCACerts));
+        }
+        if (onlySomeReasons != null)
+        {
+            appendObject(buf, sep, "onlySomeReasons", onlySomeReasons.toString());
+        }
+        if (onlyContainsAttributeCerts)
+        {
+            appendObject(buf, sep, "onlyContainsAttributeCerts", booleanToString(onlyContainsAttributeCerts));
+        }
+        if (indirectCRL)
+        {
+            appendObject(buf, sep, "indirectCRL", booleanToString(indirectCRL));
+        }
+        buf.append("]");
+        buf.append(sep);
+        return buf.toString();
+    }
+
+    private void appendObject(StringBuffer buf, String sep, String name, String value)
+    {
+        String       indent = "    ";
+
+        buf.append(indent);
+        buf.append(name);
+        buf.append(":");
+        buf.append(sep);
+        buf.append(indent);
+        buf.append(indent);
+        buf.append(value);
+        buf.append(sep);
+    }
+
+    private String booleanToString(boolean value)
+    {
+        return value ? "true" : "false";
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java b/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
deleted file mode 100644
index b247e09..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-/**
- * The KeyPurposeId object.
- * <pre>
- *     KeyPurposeId ::= OBJECT IDENTIFIER
- * </pre>
- */
-public class KeyPurposeId
-    extends DERObjectIdentifier
-{
-    private static final String id_kp = "1.3.6.1.5.5.7.3";
-
-    private KeyPurposeId(
-        String  id)
-    {
-        super(id);
-    }
-
-    public static final KeyPurposeId anyExtendedKeyUsage = new KeyPurposeId(X509Extensions.ExtendedKeyUsage.getId() + ".0");
-    public static final KeyPurposeId id_kp_serverAuth = new KeyPurposeId(id_kp + ".1");
-    public static final KeyPurposeId id_kp_clientAuth = new KeyPurposeId(id_kp + ".2");
-    public static final KeyPurposeId id_kp_codeSigning = new KeyPurposeId(id_kp + ".3");
-    public static final KeyPurposeId id_kp_emailProtection = new KeyPurposeId(id_kp + ".4");
-    public static final KeyPurposeId id_kp_ipsecEndSystem = new KeyPurposeId(id_kp + ".5");
-    public static final KeyPurposeId id_kp_ipsecTunnel = new KeyPurposeId(id_kp + ".6");
-    public static final KeyPurposeId id_kp_ipsecUser = new KeyPurposeId(id_kp + ".7");
-    public static final KeyPurposeId id_kp_timeStamping = new KeyPurposeId(id_kp + ".8");
-    public static final KeyPurposeId id_kp_OCSPSigning = new KeyPurposeId(id_kp + ".9");
-
-    //
-    // microsoft key purpose ids
-    //
-    public static final KeyPurposeId id_kp_smartcardlogon = new KeyPurposeId("1.3.6.1.4.1.311.20.2.2");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
index e56424f..3ffd94b 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -32,6 +32,21 @@
     public static final int        encipherOnly     = (1 << 0);
     public static final int        decipherOnly     = (1 << 15);
 
+    public static DERBitString getInstance(Object obj)   // needs to be DERBitString for other VMs
+    {
+        if (obj instanceof KeyUsage)
+        {
+            return (KeyUsage)obj;
+        }
+
+        if (obj instanceof X509Extension)
+        {
+            return new KeyUsage(DERBitString.getInstance(X509Extension.convertValueToObject((X509Extension)obj)));
+        }
+
+        return new KeyUsage(DERBitString.getInstance(obj));
+    }
+    
     /**
      * Basic constructor.
      * 
diff --git a/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java b/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java
deleted file mode 100644
index 0bc639a..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/NoticeReference.java
+++ /dev/null
@@ -1,155 +0,0 @@
-
-package org.bouncycastle.asn1.x509;
-
-import java.util.Enumeration;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * <code>NoticeReference</code> class, used in
- * <code>CertificatePolicies</code> X509 V3 extensions
- * (in policy qualifiers).
- * 
- * <pre>
- *  NoticeReference ::= SEQUENCE {
- *      organization     DisplayText,
- *      noticeNumbers    SEQUENCE OF INTEGER }
- *
- * </pre> 
- * 
- * @see PolicyQualifierInfo
- * @see PolicyInformation
- */
-public class NoticeReference 
-    extends ASN1Encodable
-{
-   private DisplayText organization;
-   private ASN1Sequence noticeNumbers;
-
-   /**
-    * Creates a new <code>NoticeReference</code> instance.
-    *
-    * @param orgName a <code>String</code> value
-    * @param numbers a <code>Vector</code> value
-    */
-   public NoticeReference(
-       String orgName,
-       Vector numbers) 
-   {
-      organization = new DisplayText(orgName);
-
-      Object o = numbers.elementAt(0);
-
-      ASN1EncodableVector av = new ASN1EncodableVector();
-      if (o instanceof Integer)
-      {
-         Enumeration it = numbers.elements();
-
-         while (it.hasMoreElements())
-         {
-            Integer nm = (Integer) it.nextElement();
-               DERInteger di = new DERInteger(nm.intValue());
-            av.add (di);
-         }
-      }
-
-      noticeNumbers = new DERSequence(av);
-   }
-
-   /**
-    * Creates a new <code>NoticeReference</code> instance.
-    *
-    * @param orgName a <code>String</code> value
-    * @param numbers an <code>ASN1EncodableVector</code> value
-    */
-   public NoticeReference(
-       String orgName, 
-       ASN1Sequence numbers) 
-   {
-       organization = new DisplayText (orgName);
-       noticeNumbers = numbers;
-   }
-
-   /**
-    * Creates a new <code>NoticeReference</code> instance.
-    *
-    * @param displayTextType an <code>int</code> value
-    * @param orgName a <code>String</code> value
-    * @param numbers an <code>ASN1EncodableVector</code> value
-    */
-   public NoticeReference(
-       int displayTextType,
-       String orgName,
-       ASN1Sequence numbers) 
-   {
-       organization = new DisplayText(displayTextType, 
-                                     orgName);
-       noticeNumbers = numbers;
-   }
-
-   /**
-    * Creates a new <code>NoticeReference</code> instance.
-    * <p>Useful for reconstructing a <code>NoticeReference</code>
-    * instance from its encodable/encoded form. 
-    *
-    * @param as an <code>ASN1Sequence</code> value obtained from either
-    * calling @{link toASN1Object()} for a <code>NoticeReference</code>
-    * instance or from parsing it from a DER-encoded stream. 
-    */
-   public NoticeReference(
-       ASN1Sequence as) 
-   {
-       if (as.size() != 2)
-       {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + as.size());
-       }
-
-       organization = DisplayText.getInstance(as.getObjectAt(0));
-       noticeNumbers = ASN1Sequence.getInstance(as.getObjectAt(1));
-   }
-
-   public static NoticeReference getInstance(
-       Object as) 
-   {
-      if (as instanceof NoticeReference)
-      {
-          return (NoticeReference)as;
-      }
-      else if (as instanceof ASN1Sequence)
-      {
-          return new NoticeReference((ASN1Sequence)as);
-      }
-
-      throw new IllegalArgumentException("unknown object in getInstance.");
-   }
-   
-   public DisplayText getOrganization()
-   {
-       return organization;
-   }
-   
-   public ASN1Sequence getNoticeNumbers()
-   {
-       return noticeNumbers;
-   }
-   
-   /**
-    * Describe <code>toASN1Object</code> method here.
-    *
-    * @return a <code>DERObject</code> value
-    */
-   public DERObject toASN1Object() 
-   {
-      ASN1EncodableVector av = new ASN1EncodableVector();
-      av.add (organization);
-      av.add (noticeNumbers);
-      return new DERSequence (av);
-   }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
index 048922b..b881509 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
@@ -9,12 +9,45 @@
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
-
+/**
+ * ObjectDigestInfo ASN.1 structure used in v2 attribute certificates.
+ * 
+ * <pre>
+ *  
+ *    ObjectDigestInfo ::= SEQUENCE {
+ *         digestedObjectType  ENUMERATED {
+ *                 publicKey            (0),
+ *                 publicKeyCert        (1),
+ *                 otherObjectTypes     (2) },
+ *                         -- otherObjectTypes MUST NOT
+ *                         -- be used in this profile
+ *         otherObjectTypeID   OBJECT IDENTIFIER OPTIONAL,
+ *         digestAlgorithm     AlgorithmIdentifier,
+ *         objectDigest        BIT STRING
+ *    }
+ *   
+ * </pre>
+ * 
+ */
 public class ObjectDigestInfo
     extends ASN1Encodable
 {
+    /**
+     * The public key is hashed.
+     */
+    public final static int publicKey = 0;
+
+    /**
+     * The public key certificate is hashed.
+     */
+    public final static int publicKeyCert = 1;
+
+    /**
+     * An other object is hashed.
+     */
+    public final static int otherObjectDigest = 2;
+
     DEREnumerated digestedObjectType;
 
     DERObjectIdentifier otherObjectTypeID;
@@ -24,7 +57,7 @@
     DERBitString objectDigest;
 
     public static ObjectDigestInfo getInstance(
-            Object  obj)
+        Object obj)
     {
         if (obj == null || obj instanceof ObjectDigestInfo)
         {
@@ -36,7 +69,8 @@
             return new ObjectDigestInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        throw new IllegalArgumentException("illegal object in getInstance: "
+            + obj.getClass().getName());
     }
 
     public static ObjectDigestInfo getInstance(
@@ -45,13 +79,44 @@
     {
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
-    
-    public ObjectDigestInfo(ASN1Sequence seq)
+
+    /**
+     * Constructor from given details.
+     * <p>
+     * If <code>digestedObjectType</code> is not {@link #publicKeyCert} or
+     * {@link #publicKey} <code>otherObjectTypeID</code> must be given,
+     * otherwise it is ignored.
+     * 
+     * @param digestedObjectType The digest object type.
+     * @param otherObjectTypeID The object type ID for
+     *            <code>otherObjectDigest</code>.
+     * @param digestAlgorithm The algorithm identifier for the hash.
+     * @param objectDigest The hash value.
+     */
+    public ObjectDigestInfo(
+        int digestedObjectType,
+        String otherObjectTypeID,
+        AlgorithmIdentifier digestAlgorithm,
+        byte[] objectDigest)
+    {
+        this.digestedObjectType = new DEREnumerated(digestedObjectType);
+        if (digestedObjectType == otherObjectDigest)
+        {
+            this.otherObjectTypeID = new DERObjectIdentifier(otherObjectTypeID);
+        }
+
+        this.digestAlgorithm = digestAlgorithm; 
+
+        this.objectDigest = new DERBitString(objectDigest);
+    }
+
+    private ObjectDigestInfo(
+        ASN1Sequence seq)
     {
         if (seq.size() > 4 || seq.size() < 3)
         {
             throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
+                + seq.size());
         }
 
         digestedObjectType = DEREnumerated.getInstance(seq.getObjectAt(0));
@@ -93,19 +158,19 @@
      * Produce an object suitable for an ASN1OutputStream.
      * 
      * <pre>
-     * 
-     *   ObjectDigestInfo ::= SEQUENCE {
-     *        digestedObjectType  ENUMERATED {
-     *                publicKey            (0),
-     *                publicKeyCert        (1),
-     *                otherObjectTypes     (2) },
-     *                        -- otherObjectTypes MUST NOT
-     *                        -- be used in this profile
-     *        otherObjectTypeID   OBJECT IDENTIFIER OPTIONAL,
-     *        digestAlgorithm     AlgorithmIdentifier,
-     *        objectDigest        BIT STRING
-     *   }
      *  
+     *    ObjectDigestInfo ::= SEQUENCE {
+     *         digestedObjectType  ENUMERATED {
+     *                 publicKey            (0),
+     *                 publicKeyCert        (1),
+     *                 otherObjectTypes     (2) },
+     *                         -- otherObjectTypes MUST NOT
+     *                         -- be used in this profile
+     *         otherObjectTypeID   OBJECT IDENTIFIER OPTIONAL,
+     *         digestAlgorithm     AlgorithmIdentifier,
+     *         objectDigest        BIT STRING
+     *    }
+     *   
      * </pre>
      */
     public DERObject toASN1Object()
diff --git a/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java b/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java
deleted file mode 100644
index df78ec4..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.util.Hashtable;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * PolicyMappings V3 extension, described in RFC3280.
- * <pre>
- *    PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
- *      issuerDomainPolicy      CertPolicyId,
- *      subjectDomainPolicy     CertPolicyId }
- * </pre>
- *
- * @see <a href="http://www.faqs.org/rfc/rfc3280.txt">RFC 3280, section 4.2.1.6</a>
- */
-public class PolicyMappings
-    extends ASN1Encodable
-{
-   ASN1Sequence seq = null;
-
-   /**
-    * Creates a new <code>PolicyMappings</code> instance.
-    *
-    * @param seq an <code>ASN1Sequence</code> constructed as specified
-    * in RFC 3280
-    */
-   public PolicyMappings (ASN1Sequence seq) 
-      {
-         this.seq = seq;
-      }
-
-   /**
-    * Creates a new <code>PolicyMappings</code> instance.
-    *
-    * @param mappings a <code>HashMap</code> value that maps
-    * <code>String</code> oids
-    * to other <code>String</code> oids. 
-    */
-   public PolicyMappings (Hashtable mappings) 
-      {
-         ASN1EncodableVector dev = new ASN1EncodableVector();
-         Enumeration it = mappings.keys();
-
-         while (it.hasMoreElements())
-         {
-            String idp = (String) it.nextElement();
-            String sdp = (String) mappings.get(idp);
-            ASN1EncodableVector dv = new ASN1EncodableVector();
-            dv.add(new DERObjectIdentifier(idp));
-            dv.add(new DERObjectIdentifier(sdp));
-            dev.add(new DERSequence(dv));
-         }
-
-         seq = new DERSequence(dev);
-      }
-
-   public DERObject toASN1Object() 
-      {
-         return seq;
-      }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java b/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java
deleted file mode 100644
index 2678057..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierId.java
+++ /dev/null
@@ -1,31 +0,0 @@
-
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-/**
- * PolicyQualifierId, used in the CertificatePolicies
- * X509V3 extension.
- * 
- * <pre>
- *    id-qt          OBJECT IDENTIFIER ::=  { id-pkix 2 }
- *    id-qt-cps      OBJECT IDENTIFIER ::=  { id-qt 1 }
- *    id-qt-unotice  OBJECT IDENTIFIER ::=  { id-qt 2 }
- *  PolicyQualifierId ::=
- *       OBJECT IDENTIFIER (id-qt-cps | id-qt-unotice)
- * </pre>
- */
-public class PolicyQualifierId extends DERObjectIdentifier 
-{
-   private static final String id_qt = "1.3.6.1.5.5.7.2";
-
-   private PolicyQualifierId(String id) 
-      {
-         super(id);
-      }
-   
-   public static final PolicyQualifierId id_qt_cps =
-       new PolicyQualifierId(id_qt + ".1");
-   public static final PolicyQualifierId id_qt_unotice =
-       new PolicyQualifierId(id_qt + ".2");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
deleted file mode 100644
index 6e97f70..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * Policy qualifiers, used in the X509V3 CertificatePolicies
- * extension.
- * 
- * <pre>
- *   PolicyQualifierInfo ::= SEQUENCE {
- *       policyQualifierId  PolicyQualifierId,
- *       qualifier          ANY DEFINED BY policyQualifierId }
- * </pre>
- */
-public class PolicyQualifierInfo
-    extends ASN1Encodable
-{
-   private DERObjectIdentifier policyQualifierId;
-   private DEREncodable        qualifier;
-
-   /**
-    * Creates a new <code>PolicyQualifierInfo</code> instance.
-    *
-    * @param policyQualifierId a <code>PolicyQualifierId</code> value
-    * @param qualifier the qualifier, defined by the above field.
-    */
-   public PolicyQualifierInfo(
-       DERObjectIdentifier policyQualifierId,
-       DEREncodable qualifier) 
-   {
-      this.policyQualifierId = policyQualifierId;
-      this.qualifier = qualifier;
-   }
-
-   /**
-    * Creates a new <code>PolicyQualifierInfo</code> containing a
-    * cPSuri qualifier.
-    *
-    * @param cps the CPS (certification practice statement) uri as a
-    * <code>String</code>.
-    */
-   public PolicyQualifierInfo(
-       String cps) 
-   {
-      policyQualifierId = PolicyQualifierId.id_qt_cps;
-      qualifier = new DERIA5String (cps);
-   }
-
-   /**
-    * Creates a new <code>PolicyQualifierInfo</code> instance.
-    *
-    * @param as <code>PolicyQualifierInfo</code> X509 structure
-    * encoded as an ASN1Sequence. 
-    */
-   public PolicyQualifierInfo(
-       ASN1Sequence as)
-   {
-        if (as.size() != 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + as.size());
-        }
-
-        policyQualifierId = DERObjectIdentifier.getInstance(as.getObjectAt(0));
-        qualifier = as.getObjectAt(1);
-   }
-
-   public static PolicyQualifierInfo getInstance(
-       Object as) 
-   {
-        if (as instanceof PolicyQualifierInfo)
-        {
-            return (PolicyQualifierInfo)as;
-        }
-        else if (as instanceof ASN1Sequence)
-        {
-            return new PolicyQualifierInfo((ASN1Sequence)as);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance.");
-   }
-
-
-   public DERObjectIdentifier getPolicyQualifierId()
-   {
-       return policyQualifierId;
-   }
-
-   public DEREncodable getQualifier()
-   {
-       return qualifier;
-   }
-   
-   /**
-    * Returns a DER-encodable representation of this instance. 
-    *
-    * @return a <code>DERObject</code> value
-    */
-   public DERObject toASN1Object() 
-   {
-      ASN1EncodableVector dev = new ASN1EncodableVector();
-      dev.add(policyQualifierId);
-      dev.add(qualifier);
-
-      return new DERSequence(dev);
-   }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java b/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java
deleted file mode 100644
index fe227d9..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/RoleSyntax.java
+++ /dev/null
@@ -1,236 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERString;
-import org.bouncycastle.asn1.DERTaggedObject;
-
-/**
- * Implementation of the RoleSyntax object as specified by the RFC3281.
- * 
- * <pre>
- * RoleSyntax ::= SEQUENCE {
- *                 roleAuthority  [0] GeneralNames OPTIONAL,
- *                 roleName       [1] GeneralName
- *           } 
- * </pre>
- */
-public class RoleSyntax 
-    extends ASN1Encodable
-{
-    private GeneralNames roleAuthority;
-    private GeneralName roleName;
-
-    /**
-     * RoleSyntax factory method.
-     * @param obj the object used to construct an instance of <code>
-     * RoleSyntax</code>. It must be an instance of <code>RoleSyntax
-     * </code> or <code>ASN1Sequence</code>.
-     * @return the instance of <code>RoleSyntax</code> built from the
-     * supplied object.
-     * @throws java.lang.IllegalArgumentException if the object passed
-     * to the factory is not an instance of <code>RoleSyntax</code> or
-     * <code>ASN1Sequence</code>.
-     */
-    public static RoleSyntax getInstance(
-        Object obj)
-    {
-        
-        if(obj == null || obj instanceof RoleSyntax)
-        {
-            return (RoleSyntax)obj;
-        }
-        else if(obj instanceof ASN1Sequence)
-        {
-            return new RoleSyntax((ASN1Sequence)obj);
-        }
-        throw new IllegalArgumentException("Unknown object in RoleSyntax factory.");
-    }
-    
-    /**
-     * Constructor.
-     * @param roleAuthority the role authority of this RoleSyntax.
-     * @param roleName    the role name of this RoleSyntax.
-     */
-    public RoleSyntax(
-        GeneralNames roleAuthority,
-        GeneralName roleName)
-    {
-        if(roleName == null || 
-                roleName.getTagNo() != GeneralName.uniformResourceIdentifier ||
-                ((DERString)roleName.getName()).getString().equals(""))
-        {
-            throw new IllegalArgumentException("the role name MUST be non empty and MUST " +
-                    "use the URI option of GeneralName");
-        }
-        this.roleAuthority = roleAuthority;
-        this.roleName = roleName;
-    }
-    
-    /**
-     * Constructor. Invoking this constructor is the same as invoking
-     * <code>new RoleSyntax(null, roleName)</code>.
-     * @param roleName    the role name of this RoleSyntax.
-     */
-    public RoleSyntax(
-        GeneralName roleName)
-    {
-        this(null, roleName);
-    }
-
-    /**
-     * Utility constructor. Takes a <code>String</code> argument representing
-     * the role name, builds a <code>GeneralName</code> to hold the role name
-     * and calls the constructor that takes a <code>GeneralName</code>.
-     * @param roleName
-     */
-    public RoleSyntax(
-        String roleName)
-    {
-        this(new GeneralName(GeneralName.uniformResourceIdentifier,
-                (roleName == null)? "": roleName));
-    }
-    
-    /**
-     * Constructor that builds an instance of <code>RoleSyntax</code> by
-     * extracting the encoded elements from the <code>ASN1Sequence</code>
-     * object supplied.
-     * @param seq    an instance of <code>ASN1Sequence</code> that holds
-     * the encoded elements used to build this <code>RoleSyntax</code>.
-     */
-    public RoleSyntax(
-        ASN1Sequence seq)
-    {
-        if (seq.size() < 1 || seq.size() > 2)
-        {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
-        }
-
-        for (int i = 0; i != seq.size(); i++)
-        {
-            ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
-            switch (taggedObject.getTagNo())
-            {
-            case 0:
-                roleAuthority = GeneralNames.getInstance(taggedObject, false);
-                break;
-            case 1:
-                roleName = GeneralName.getInstance(taggedObject, false);
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown tag in RoleSyntax");
-            }
-        }
-    }
-
-    /**
-     * Gets the role authority of this RoleSyntax.
-     * @return    an instance of <code>GeneralNames</code> holding the
-     * role authority of this RoleSyntax.
-     */
-    public GeneralNames getRoleAuthority()
-    {
-        return this.roleAuthority;
-    }
-    
-    /**
-     * Gets the role name of this RoleSyntax.
-     * @return    an instance of <code>GeneralName</code> holding the
-     * role name of this RoleSyntax.
-     */
-    public GeneralName getRoleName()
-    {
-        return this.roleName;
-    }
-    
-    /**
-     * Gets the role name as a <code>java.lang.String</code> object.
-     * @return    the role name of this RoleSyntax represented as a 
-     * <code>java.lang.String</code> object.
-     */
-    public String getRoleNameAsString()
-    {
-        DERString str = (DERString)this.roleName.getName();
-        
-        return str.getString();
-    }
-    
-    /**
-     * Gets the role authority as a <code>String[]</code> object.
-     * @return the role authority of this RoleSyntax represented as a
-     * <code>String[]<code> array.
-     */
-    public String[] getRoleAuthorityAsString() 
-    {
-        if(roleAuthority == null) 
-        {
-            return new String[0];
-        }
-        
-        GeneralName[] names = roleAuthority.getNames();
-        String[] namesString = new String[names.length];
-        for(int i = 0; i < names.length; i++) 
-        {
-            DEREncodable value = names[i].getName();
-            if(value instanceof DERString)
-            {
-                namesString[i] = ((DERString)value).getString();
-            }
-            else
-            {
-                namesString[i] = value.toString();
-            }
-        }
-        return namesString;
-    }
-    
-    /**
-     * Implementation of the method <code>toASN1Object</code> as
-     * required by the superclass <code>ASN1Encodable</code>.
-     * 
-     * <pre>
-     * RoleSyntax ::= SEQUENCE {
-     *                 roleAuthority  [0] GeneralNames OPTIONAL,
-     *                 roleName       [1] GeneralName
-     *           } 
-     * </pre>
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        if(this.roleAuthority != null)
-        {
-            v.add(new DERTaggedObject(false, 0, roleAuthority));
-        }
-        v.add(new DERTaggedObject(false, 1, roleName));
-        
-        return new DERSequence(v);
-    }
-    
-    public String toString() 
-    {
-        StringBuffer buff = new StringBuffer("Name: " + this.getRoleNameAsString() +
-                " - Auth: ");
-        if(this.roleAuthority == null || roleAuthority.getNames().length == 0)
-        {
-            buff.append("N/A");
-        }
-        else 
-        {
-            String[] names = this.getRoleAuthorityAsString();
-            buff.append('[').append(names[0]);
-            for(int i = 1; i < names.length; i++) 
-            {
-                    buff.append(", ").append(names[i]);
-            }
-            buff.append(']');
-        }
-        return buff.toString();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java b/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java
deleted file mode 100644
index 3dede65..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.util.Enumeration;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * This extension may contain further X.500 attributes of the subject. See also
- * RFC 3039.
- * 
- * <pre>
- *     SubjectDirectoryAttributes ::= Attributes
- *     Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
- *     Attribute ::= SEQUENCE 
- *     {
- *       type AttributeType 
- *       values SET OF AttributeValue 
- *     }
- *     
- *     AttributeType ::= OBJECT IDENTIFIER
- *     AttributeValue ::= ANY DEFINED BY AttributeType
- * </pre>
- * 
- * @see org.bouncycastle.asn1.x509.X509Name for AttributeType ObjectIdentifiers.
- */
-public class SubjectDirectoryAttributes 
-    extends ASN1Encodable
-{
-    private Vector attributes = new Vector();
-
-    public static SubjectDirectoryAttributes getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof SubjectDirectoryAttributes)
-        {
-            return (SubjectDirectoryAttributes)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new SubjectDirectoryAttributes((ASN1Sequence)obj);
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Constructor from ASN1Sequence.
-     * 
-     * The sequence is of type SubjectDirectoryAttributes:
-     * 
-     * <pre>
-     *      SubjectDirectoryAttributes ::= Attributes
-     *      Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
-     *      Attribute ::= SEQUENCE 
-     *      {
-     *        type AttributeType 
-     *        values SET OF AttributeValue 
-     *      }
-     *      
-     *      AttributeType ::= OBJECT IDENTIFIER
-     *      AttributeValue ::= ANY DEFINED BY AttributeType
-     * </pre>
-     * 
-     * @param seq
-     *            The ASN.1 sequence.
-     */
-    public SubjectDirectoryAttributes(ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
-            attributes.addElement(new Attribute(s));
-        }
-    }
-
-    /**
-     * Constructor from a vector of attributes.
-     * 
-     * The vector consists of attributes of type {@link Attribute Attribute}
-     * 
-     * @param attributes
-     *            The attributes.
-     * 
-     */
-    public SubjectDirectoryAttributes(Vector attributes)
-    {
-        Enumeration e = attributes.elements();
-
-        while (e.hasMoreElements())
-        {
-            this.attributes.addElement(e.nextElement());
-        }
-    }
-
-    /**
-     * Produce an object suitable for an ASN1OutputStream.
-     * 
-     * Returns:
-     * 
-     * <pre>
-     *      SubjectDirectoryAttributes ::= Attributes
-     *      Attributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
-     *      Attribute ::= SEQUENCE 
-     *      {
-     *        type AttributeType 
-     *        values SET OF AttributeValue 
-     *      }
-     *      
-     *      AttributeType ::= OBJECT IDENTIFIER
-     *      AttributeValue ::= ANY DEFINED BY AttributeType
-     * </pre>
-     * 
-     * @return a DERObject
-     */
-    public DERObject toASN1Object()
-    {
-        ASN1EncodableVector vec = new ASN1EncodableVector();
-        Enumeration e = attributes.elements();
-
-        while (e.hasMoreElements())
-        {
-
-            vec.add((Attribute)e.nextElement());
-        }
-
-        return new DERSequence(vec);
-    }
-
-    /**
-     * @return Returns the attributes.
-     */
-    public Vector getAttributes()
-    {
-        return attributes;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java b/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
index ca7ad42..8701b1e 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
@@ -29,7 +29,7 @@
     public static SubjectKeyIdentifier getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof SubjectKeyIdentifier) 
+        if (obj instanceof SubjectKeyIdentifier)
         {
             return (SubjectKeyIdentifier)obj;
         }
@@ -43,7 +43,12 @@
         {
             return new SubjectKeyIdentifier((ASN1OctetString)obj);
         }
-        
+
+        if (obj instanceof X509Extension)
+        {
+            return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
+        }
+
         throw new IllegalArgumentException("Invalid SubjectKeyIdentifier: " + obj.getClass().getName());
     }
     
@@ -57,25 +62,18 @@
         ASN1OctetString  keyid)
     {
         this.keyidentifier=keyid.getOctets();
-
     }
 
     /**
+     * Calculates the keyidentifier using a SHA1 hash over the BIT STRING
+     * from SubjectPublicKeyInfo as defined in RFC3280.
      *
-     * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
-     * from SubjectPublicKeyInfo as defined in RFC2459.
-     *
-     **/
+     * @param spki the subject public key info.
+     */
     public SubjectKeyIdentifier(
         SubjectPublicKeyInfo    spki)
     {
-        Digest  digest = new SHA1Digest();
-        byte[]  resBuf = new byte[digest.getDigestSize()];
-
-        byte[] bytes = spki.getPublicKeyData().getBytes();
-        digest.update(bytes, 0, bytes.length);
-        digest.doFinal(resBuf, 0);
-        this.keyidentifier=resBuf;
+        this.keyidentifier = getDigest(spki);
     }
 
     public byte[] getKeyIdentifier()
@@ -87,4 +85,53 @@
     {
         return new DEROctetString(keyidentifier);
     }
+
+    /**
+     * Return a RFC 3280 type 1 key identifier. As in:
+     * <pre>
+     * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+     * value of the BIT STRING subjectPublicKey (excluding the tag,
+     * length, and number of unused bits).
+     * </pre>
+     * @param keyInfo the key info object containing the subjectPublicKey field.
+     * @return the key identifier.
+     */
+    public static SubjectKeyIdentifier createSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
+    {
+        return new SubjectKeyIdentifier(keyInfo);
+    }
+
+    /**
+     * Return a RFC 3280 type 2 key identifier. As in:
+     * <pre>
+     * (2) The keyIdentifier is composed of a four bit type field with
+     * the value 0100 followed by the least significant 60 bits of the
+     * SHA-1 hash of the value of the BIT STRING subjectPublicKey.
+     * </pre>
+     * @param keyInfo the key info object containing the subjectPublicKey field.
+     * @return the key identifier.
+     */
+    public static SubjectKeyIdentifier createTruncatedSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
+    {
+        byte[] dig = getDigest(keyInfo);
+        byte[] id = new byte[8];
+
+        System.arraycopy(dig, dig.length - 8, id, 0, id.length);
+
+        id[0] &= 0x0f;
+        id[0] |= 0x40;
+        
+        return new SubjectKeyIdentifier(id);
+    }
+
+    private static byte[] getDigest(SubjectPublicKeyInfo spki)
+    {
+        Digest digest = new SHA1Digest();
+        byte[]  resBuf = new byte[digest.getDigestSize()];
+
+        byte[] bytes = spki.getPublicKeyData().getBytes();
+        digest.update(bytes, 0, bytes.length);
+        digest.doFinal(resBuf, 0);
+        return resBuf;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
index a733727..d122327 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -44,7 +44,7 @@
             return new SubjectPublicKeyInfo((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public SubjectPublicKeyInfo(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java b/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
index 6c5afd8..f14e9bb 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
@@ -9,6 +9,8 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.DERUTCTime;
 
+import java.util.Enumeration;
+
 /**
  * PKIX RFC-2459 - TBSCertList object.
  * <pre>
@@ -54,10 +56,6 @@
 
             userCertificate = DERInteger.getInstance(seq.getObjectAt(0));
             revocationDate = Time.getInstance(seq.getObjectAt(1));
-            if (seq.size() == 3)
-            {
-                crlEntryExtensions = X509Extensions.getInstance(seq.getObjectAt(2));
-            }
         }
 
         public DERInteger getUserCertificate()
@@ -72,6 +70,11 @@
 
         public X509Extensions getExtensions()
         {
+            if (crlEntryExtensions == null && seq.size() == 3)
+            {
+                crlEntryExtensions = X509Extensions.getInstance(seq.getObjectAt(2));
+            }
+            
             return crlEntryExtensions;
         }
 
@@ -81,6 +84,41 @@
         }
     }
 
+    private class RevokedCertificatesEnumeration
+        implements Enumeration
+    {
+        private final Enumeration en;
+
+        RevokedCertificatesEnumeration(Enumeration en)
+        {
+            this.en = en;
+        }
+
+        public boolean hasMoreElements()
+        {
+            return en.hasMoreElements();
+        }
+
+        public Object nextElement()
+        {
+            return new CRLEntry(ASN1Sequence.getInstance(en.nextElement()));
+        }
+    }
+
+    private class EmptyEnumeration
+        implements Enumeration
+    {
+        public boolean hasMoreElements()
+        {
+            return false;
+        }
+
+        public Object nextElement()
+        {
+            return null;   // TODO: check exception handling
+        }
+    }
+
     ASN1Sequence     seq;
 
     DERInteger              version;
@@ -88,7 +126,7 @@
     X509Name                issuer;
     Time                    thisUpdate;
     Time                    nextUpdate;
-    CRLEntry[]              revokedCertificates;
+    ASN1Sequence            revokedCertificates;
     X509Extensions          crlExtensions;
 
     public static TBSCertList getInstance(
@@ -110,7 +148,7 @@
             return new TBSCertList((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public TBSCertList(
@@ -149,19 +187,13 @@
         if (seqPos < seq.size()
             && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject))
         {
-            ASN1Sequence certs = ASN1Sequence.getInstance(seq.getObjectAt(seqPos++));
-            revokedCertificates = new CRLEntry[certs.size()];
-
-            for (int i = 0; i < revokedCertificates.length; i++)
-            {
-                revokedCertificates[i] = new CRLEntry(ASN1Sequence.getInstance(certs.getObjectAt(i)));
-            }
+            revokedCertificates = ASN1Sequence.getInstance(seq.getObjectAt(seqPos++));
         }
 
         if (seqPos < seq.size()
             && seq.getObjectAt(seqPos) instanceof DERTaggedObject)
         {
-            crlExtensions = X509Extensions.getInstance(seq.getObjectAt(seqPos++));
+            crlExtensions = X509Extensions.getInstance(seq.getObjectAt(seqPos));
         }
     }
 
@@ -197,7 +229,29 @@
 
     public CRLEntry[] getRevokedCertificates()
     {
-        return revokedCertificates;
+        if (revokedCertificates == null)
+        {
+            return new CRLEntry[0];
+        }
+
+        CRLEntry[] entries = new CRLEntry[revokedCertificates.size()];
+
+        for (int i = 0; i < entries.length; i++)
+        {
+            entries[i] = new CRLEntry(ASN1Sequence.getInstance(revokedCertificates.getObjectAt(i)));
+        }
+        
+        return entries;
+    }
+
+    public Enumeration getRevokedCertificateEnumeration()
+    {
+        if (revokedCertificates == null)
+        {
+            return new EmptyEnumeration();
+        }
+
+        return new RevokedCertificatesEnumeration(revokedCertificates.getObjects());
     }
 
     public X509Extensions getExtensions()
diff --git a/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
index cc3c0e4..f1a6cfd 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -65,7 +65,7 @@
             return new TBSCertificateStructure((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public TBSCertificateStructure(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Time.java b/src/main/java/org/bouncycastle/asn1/x509/Time.java
index 7662ee4..c05c65d 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/Time.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/Time.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1.x509;
 
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.SimpleTimeZone;
-
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1TaggedObject;
@@ -12,6 +7,11 @@
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERUTCTime;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
 public class Time
     extends ASN1Encodable
     implements ASN1Choice
@@ -79,7 +79,7 @@
             return new Time((DERGeneralizedTime)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     public String getTime()
@@ -96,9 +96,21 @@
 
     public Date getDate()
     {
-        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
-
-        return dateF.parse(this.getTime(), new ParsePosition(0));
+        try
+        {
+            if (time instanceof DERUTCTime)
+            {
+                return ((DERUTCTime)time).getAdjustedDate();
+            }
+            else
+            {
+                return ((DERGeneralizedTime)time).getDate();
+            }
+        }
+        catch (ParseException e)
+        {         // this should never happen
+            throw new IllegalStateException("invalid date string: " + e.getMessage());
+        }
     }
 
     /**
@@ -113,4 +125,9 @@
     {
         return time;
     }
+
+    public String toString()
+    {
+        return getTime();
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java b/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
deleted file mode 100644
index b3785ff..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/UserNotice.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * <code>UserNotice</code> class, used in
- * <code>CertificatePolicies</code> X509 extensions (in policy
- * qualifiers).
- * <pre>
- * UserNotice ::= SEQUENCE {
- *      noticeRef        NoticeReference OPTIONAL,
- *      explicitText     DisplayText OPTIONAL}
- *
- * </pre>
- * 
- * @see PolicyQualifierId
- * @see PolicyInformation
- */
-public class UserNotice 
-    extends ASN1Encodable 
-{
-    private NoticeReference noticeRef;
-    private DisplayText     explicitText;
-   
-    /**
-     * Creates a new <code>UserNotice</code> instance.
-     *
-     * @param noticeRef a <code>NoticeReference</code> value
-     * @param explicitText a <code>DisplayText</code> value
-     */
-    public UserNotice(
-        NoticeReference noticeRef, 
-        DisplayText explicitText) 
-    {
-        this.noticeRef = noticeRef;
-        this.explicitText = explicitText;
-    }
-
-    /**
-     * Creates a new <code>UserNotice</code> instance.
-     *
-     * @param noticeRef a <code>NoticeReference</code> value
-     * @param str the explicitText field as a String. 
-     */
-    public UserNotice(
-        NoticeReference noticeRef, 
-        String str) 
-    {
-        this.noticeRef = noticeRef;
-        this.explicitText = new DisplayText(str);
-    }
-
-    /**
-     * Creates a new <code>UserNotice</code> instance.
-     * <p>Useful from reconstructing a <code>UserNotice</code> instance
-     * from its encodable/encoded form. 
-     *
-     * @param as an <code>ASN1Sequence</code> value obtained from either
-     * calling @{link toASN1Object()} for a <code>UserNotice</code>
-     * instance or from parsing it from a DER-encoded stream. 
-     */
-    public UserNotice(
-       ASN1Sequence as) 
-    {
-       if (as.size() == 2)
-       {
-           noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
-           explicitText = DisplayText.getInstance(as.getObjectAt(1));
-       }
-       else if (as.size() == 1)
-       {
-           if (as.getObjectAt(0).getDERObject() instanceof ASN1Sequence)
-           {
-               noticeRef = NoticeReference.getInstance(as.getObjectAt(0));
-           }
-           else
-           {
-               explicitText = DisplayText.getInstance(as.getObjectAt(0));
-           }
-       }
-       else
-       {
-           throw new IllegalArgumentException("Bad sequence size: " + as.size());
-       }
-    }
-   
-    public NoticeReference getNoticeRef()
-    {
-        return noticeRef;
-    }
-    
-    public DisplayText getExplicitText()
-    {
-        return explicitText;
-    }
-    
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector av = new ASN1EncodableVector();
-      
-        if (noticeRef != null)
-        {
-            av.add(noticeRef);
-        }
-        
-        if (explicitText != null)
-        {
-            av.add(explicitText);
-        }
-         
-        return new DERSequence(av);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java b/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
deleted file mode 100644
index 53505d1..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.DERUTCTime;
-
-/**
- * Generator for Version 1 TBSCertificateStructures.
- * <pre>
- * TBSCertificate ::= SEQUENCE {
- *      version          [ 0 ]  Version DEFAULT v1(0),
- *      serialNumber            CertificateSerialNumber,
- *      signature               AlgorithmIdentifier,
- *      issuer                  Name,
- *      validity                Validity,
- *      subject                 Name,
- *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
- *      }
- * </pre>
- *
- */
-public class V1TBSCertificateGenerator
-{
-    DERTaggedObject         version = new DERTaggedObject(0, new DERInteger(0));
-
-    DERInteger              serialNumber;
-    AlgorithmIdentifier     signature;
-    X509Name                issuer;
-    Time                    startDate, endDate;
-    X509Name                subject;
-    SubjectPublicKeyInfo    subjectPublicKeyInfo;
-
-    public V1TBSCertificateGenerator()
-    {
-    }
-
-    public void setSerialNumber(
-        DERInteger  serialNumber)
-    {
-        this.serialNumber = serialNumber;
-    }
-
-    public void setSignature(
-        AlgorithmIdentifier    signature)
-    {
-        this.signature = signature;
-    }
-
-    public void setIssuer(
-        X509Name    issuer)
-    {
-        this.issuer = issuer;
-    }
-
-    public void setStartDate(
-        Time startDate)
-    {
-        this.startDate = startDate;
-    }
-
-    public void setStartDate(
-        DERUTCTime startDate)
-    {
-        this.startDate = new Time(startDate);
-    }
-
-    public void setEndDate(
-        Time endDate)
-    {
-        this.endDate = endDate;
-    }
-
-    public void setEndDate(
-        DERUTCTime endDate)
-    {
-        this.endDate = new Time(endDate);
-    }
-
-    public void setSubject(
-        X509Name    subject)
-    {
-        this.subject = subject;
-    }
-
-    public void setSubjectPublicKeyInfo(
-        SubjectPublicKeyInfo    pubKeyInfo)
-    {
-        this.subjectPublicKeyInfo = pubKeyInfo;
-    }
-
-    public TBSCertificateStructure generateTBSCertificate()
-    {
-        if ((serialNumber == null) || (signature == null)
-            || (issuer == null) || (startDate == null) || (endDate == null)
-            || (subject == null) || (subjectPublicKeyInfo == null))
-        {
-            throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator");
-        }
-
-        ASN1EncodableVector  seq = new ASN1EncodableVector();
-
-        // seq.add(version); - not required as default value.
-        seq.add(serialNumber);
-        seq.add(signature);
-        seq.add(issuer);
-
-        //
-        // before and after dates
-        //
-        ASN1EncodableVector  validity = new ASN1EncodableVector();
-
-        validity.add(startDate);
-        validity.add(endDate);
-
-        seq.add(new DERSequence(validity));
-
-        seq.add(subject);
-
-        seq.add(subjectPublicKeyInfo);
-
-        return new TBSCertificateStructure(new DERSequence(seq));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java b/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
deleted file mode 100644
index 5931f77..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERSet;
-
-/**
- * Generator for Version 2 AttributeCertificateInfo
- * <pre>
- * AttributeCertificateInfo ::= SEQUENCE {
- *       version              AttCertVersion -- version is v2,
- *       holder               Holder,
- *       issuer               AttCertIssuer,
- *       signature            AlgorithmIdentifier,
- *       serialNumber         CertificateSerialNumber,
- *       attrCertValidityPeriod   AttCertValidityPeriod,
- *       attributes           SEQUENCE OF Attribute,
- *       issuerUniqueID       UniqueIdentifier OPTIONAL,
- *       extensions           Extensions OPTIONAL
- * }
- * </pre>
- *
- */
-public class V2AttributeCertificateInfoGenerator
-{
-    private DERInteger version;
-    private Holder holder;
-    private AttCertIssuer issuer;
-    private AlgorithmIdentifier signature;
-    private DERInteger serialNumber;
-    private AttCertValidityPeriod attrCertValidityPeriod;
-    private ASN1EncodableVector attributes;
-    private DERBitString issuerUniqueID;
-    private X509Extensions extensions;
-    private DERGeneralizedTime startDate, endDate;
-
-    public V2AttributeCertificateInfoGenerator()
-    {
-        this.version = new DERInteger(1);
-        attributes = new ASN1EncodableVector();
-    }
-    
-    public void setHolder(Holder holder)
-    {
-        this.holder = holder;
-    }
-    
-    public void addAttribute(String oid, ASN1Encodable value) 
-    {
-        attributes.add(new Attribute(new DERObjectIdentifier(oid), new DERSet(value)));
-    }
-
-    /**
-     * @param attribute
-     */
-    public void addAttribute(Attribute attribute)
-    {
-        attributes.add(attribute);
-    }
-    
-    public void setSerialNumber(
-        DERInteger  serialNumber)
-    {
-        this.serialNumber = serialNumber;
-    }
-
-    public void setSignature(
-        AlgorithmIdentifier    signature)
-    {
-        this.signature = signature;
-    }
-
-    public void setIssuer(
-        AttCertIssuer    issuer)
-    {
-        this.issuer = issuer;
-    }
-
-    public void setStartDate(
-        DERGeneralizedTime startDate)
-    {
-        this.startDate = startDate;
-    }
-
-    public void setEndDate(
-        DERGeneralizedTime endDate)
-    {
-        this.endDate = endDate;
-    }
-
-    public void setIssuerUniqueID(
-        DERBitString    issuerUniqueID)
-    {
-        this.issuerUniqueID = issuerUniqueID;
-    }
-
-    public void setExtensions(
-        X509Extensions    extensions)
-    {
-        this.extensions = extensions;
-    }
-
-    public AttributeCertificateInfo generateAttributeCertificateInfo()
-    {
-        if ((serialNumber == null) || (signature == null)
-            || (issuer == null) || (startDate == null) || (endDate == null)
-            || (holder == null) || (attributes == null))
-        {
-            throw new IllegalStateException("not all mandatory fields set in V2 AttributeCertificateInfo generator");
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(holder);
-        v.add(issuer);
-        v.add(signature);
-        v.add(serialNumber);
-    
-        //
-        // before and after dates => AttCertValidityPeriod
-        //
-        AttCertValidityPeriod validity = new AttCertValidityPeriod(startDate, endDate);
-        v.add(validity);
-        
-        // Attributes
-        v.add(new DERSequence(attributes));
-        
-        if (issuerUniqueID != null)
-        {
-            v.add(issuerUniqueID);
-        }
-    
-        if (extensions != null)
-        {
-            v.add(extensions);
-        }
-
-        return new AttributeCertificateInfo(new DERSequence(v));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V2Form.java b/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
index 6a229dd..1eb77d1 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
@@ -34,7 +34,7 @@
             return new V2Form((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
     
     public V2Form(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java b/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java
deleted file mode 100644
index f50a3b8..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.DERUTCTime;
-
-/**
- * Generator for Version 2 TBSCertList structures.
- * <pre>
- *  TBSCertList  ::=  SEQUENCE  {
- *       version                 Version OPTIONAL,
- *                                    -- if present, shall be v2
- *       signature               AlgorithmIdentifier,
- *       issuer                  Name,
- *       thisUpdate              Time,
- *       nextUpdate              Time OPTIONAL,
- *       revokedCertificates     SEQUENCE OF SEQUENCE  {
- *            userCertificate         CertificateSerialNumber,
- *            revocationDate          Time,
- *            crlEntryExtensions      Extensions OPTIONAL
- *                                          -- if present, shall be v2
- *                                 }  OPTIONAL,
- *       crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
- *                                          -- if present, shall be v2
- *                                 }
- * </pre>
- *
- * <b>Note: This class may be subject to change</b>
- */
-public class V2TBSCertListGenerator
-{
-    DERInteger version = new DERInteger(1);
-
-    AlgorithmIdentifier     signature;
-    X509Name                issuer;
-    Time                    thisUpdate, nextUpdate=null;
-    X509Extensions          extensions=null;
-    private Vector          crlentries=null;
-
-    public V2TBSCertListGenerator()
-    {
-    }
-
-
-    public void setSignature(
-        AlgorithmIdentifier    signature)
-    {
-        this.signature = signature;
-    }
-
-    public void setIssuer(
-        X509Name    issuer)
-    {
-        this.issuer = issuer;
-    }
-
-    public void setThisUpdate(
-        DERUTCTime thisUpdate)
-    {
-        this.thisUpdate = new Time(thisUpdate);
-    }
-
-    public void setNextUpdate(
-        DERUTCTime nextUpdate)
-    {
-        this.nextUpdate = new Time(nextUpdate);
-    }
-
-    public void setThisUpdate(
-        Time thisUpdate)
-    {
-        this.thisUpdate = thisUpdate;
-    }
-
-    public void setNextUpdate(
-        Time nextUpdate)
-    {
-        this.nextUpdate = nextUpdate;
-    }
-
-    public void addCRLEntry(
-        ASN1Sequence crlEntry)
-    {
-        if (crlentries == null)
-        {
-            crlentries = new Vector();
-        }
-        
-        crlentries.addElement(crlEntry);
-    }
-
-    public void addCRLEntry(DERInteger userCertificate, DERUTCTime revocationDate, int reason)
-    {
-        addCRLEntry(userCertificate, new Time(revocationDate), reason);
-    }
-
-    public void addCRLEntry(DERInteger userCertificate, Time revocationDate, int reason)
-    {
-        addCRLEntry(userCertificate, revocationDate, reason, null);
-    }
-
-    public void addCRLEntry(DERInteger userCertificate, Time revocationDate, int reason, DERGeneralizedTime invalidityDate)
-    {
-        Vector extOids = new Vector();
-        Vector extValues = new Vector();
-        
-        if (reason != 0)
-        {
-            CRLReason crlReason = new CRLReason(reason);
-            
-            try
-            {
-                extOids.addElement(X509Extensions.ReasonCode);
-                extValues.addElement(new X509Extension(false, new DEROctetString(crlReason.getEncoded())));
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("error encoding reason: " + e);
-            }
-        }
-
-        if (invalidityDate != null)
-        {
-            try
-            {
-                extOids.addElement(X509Extensions.InvalidityDate);
-                extValues.addElement(new X509Extension(false, new DEROctetString(invalidityDate.getEncoded())));
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("error encoding invalidityDate: " + e);
-            }
-        }
-        
-        if (extOids.size() != 0)
-        {
-            addCRLEntry(userCertificate, revocationDate, new X509Extensions(extOids, extValues));
-        }
-        else
-        {
-            addCRLEntry(userCertificate, revocationDate, null);
-        }
-    }
-
-    public void addCRLEntry(DERInteger userCertificate, Time revocationDate, X509Extensions extensions)
-    {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(userCertificate);
-        v.add(revocationDate);
-        
-        if (extensions != null)
-        {
-            v.add(extensions);
-        }
-        
-        addCRLEntry(new DERSequence(v));
-    }
-    
-    public void setExtensions(
-        X509Extensions    extensions)
-    {
-        this.extensions = extensions;
-    }
-
-    public TBSCertList generateTBSCertList()
-    {
-        if ((signature == null) || (issuer == null) || (thisUpdate == null))
-        {
-            throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator.");
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(version);
-        v.add(signature);
-        v.add(issuer);
-
-        v.add(thisUpdate);
-        if (nextUpdate != null)
-        {
-            v.add(nextUpdate);
-        }
-
-        // Add CRLEntries if they exist
-        if (crlentries != null)
-        {
-            ASN1EncodableVector certs = new ASN1EncodableVector();
-            Enumeration it = crlentries.elements();
-            while(it.hasMoreElements())
-            {
-                certs.add((ASN1Sequence)it.nextElement());
-            }
-            v.add(new DERSequence(certs));
-        }
-
-        if (extensions != null)
-        {
-            v.add(new DERTaggedObject(0, extensions));
-        }
-
-        return new TBSCertList(new DERSequence(v));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
index 149d680..9d8ba05 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.asn1.x509;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
@@ -36,6 +37,10 @@
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
     X509Extensions          extensions;
 
+    private boolean altNamePresentAndCritical;
+    private DERBitString issuerUniqueID;
+    private DERBitString subjectUniqueID;
+
     public V3TBSCertificateGenerator()
     {
     }
@@ -88,6 +93,18 @@
         this.subject = subject;
     }
 
+    public void setIssuerUniqueID(
+        DERBitString uniqueID)
+    {
+        this.issuerUniqueID = uniqueID;
+    }
+
+    public void setSubjectUniqueID(
+        DERBitString uniqueID)
+    {
+        this.subjectUniqueID = uniqueID;
+    }
+
     public void setSubjectPublicKeyInfo(
         SubjectPublicKeyInfo    pubKeyInfo)
     {
@@ -98,13 +115,22 @@
         X509Extensions    extensions)
     {
         this.extensions = extensions;
+        if (extensions != null)
+        {
+            X509Extension altName = extensions.getExtension(X509Extensions.SubjectAlternativeName);
+
+            if (altName != null && altName.isCritical())
+            {
+                altNamePresentAndCritical = true;
+            }
+        }
     }
 
     public TBSCertificateStructure generateTBSCertificate()
     {
         if ((serialNumber == null) || (signature == null)
             || (issuer == null) || (startDate == null) || (endDate == null)
-            || (subject == null) || (subjectPublicKeyInfo == null))
+            || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
         {
             throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
         }
@@ -126,10 +152,27 @@
 
         v.add(new DERSequence(validity));
 
-        v.add(subject);
+        if (subject != null)
+        {
+            v.add(subject);
+        }
+        else
+        {
+            v.add(new DERSequence());
+        }
 
         v.add(subjectPublicKeyInfo);
 
+        if (issuerUniqueID != null)
+        {
+            v.add(new DERTaggedObject(false, 1, issuerUniqueID));
+        }
+
+        if (subjectUniqueID != null)
+        {
+            v.add(new DERTaggedObject(false, 2, subjectUniqueID));
+        }
+
         if (extensions != null)
         {
             v.add(new DERTaggedObject(3, extensions));
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Attributes.java b/src/main/java/org/bouncycastle/asn1/x509/X509Attributes.java
deleted file mode 100644
index 8ea18fa..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/X509Attributes.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.bouncycastle.asn1.x509;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public class X509Attributes
-{
-    public static final DERObjectIdentifier RoleSyntax = new DERObjectIdentifier("2.5.4.72");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
index 599db32..347b661 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
@@ -46,7 +46,12 @@
             return new X509CertificateStructure((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory");
+        if (obj != null)
+        {
+            throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+        }
+
+        throw new IllegalArgumentException("null object in factory");
     }
 
     public X509CertificateStructure(
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
index a9402b6..6098c27 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -1,13 +1,14 @@
 package org.bouncycastle.asn1.x509;
 
-import java.io.IOException;
-
 import org.bouncycastle.asn1.DERGeneralizedTime;
 import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERPrintableString;
 import org.bouncycastle.asn1.DERUTF8String;
 
+import java.io.IOException;
+
 /**
  * The default converter for X509 DN entries when going from their
  * string value to ASN.1 strings.
@@ -38,13 +39,25 @@
                 throw new RuntimeException("can't recode value for oid " + oid.getId());
             }
         }
-        else if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+        else
         {
-            return new DERIA5String(value);
-        }
-        else if (oid.equals(X509Name.DATE_OF_BIRTH))
-        {
-            return new DERGeneralizedTime(value);
+            if (value.length() != 0 && value.charAt(0) == '\\')
+            {
+                value = value.substring(1);
+            }
+            if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+            {
+                return new DERIA5String(value);
+            }
+            else if (oid.equals(X509Name.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
+            {
+                return new DERGeneralizedTime(value);
+            }
+            else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER)
+                || oid.equals(X509Name.TELEPHONE_NUMBER))
+            {
+                 return new DERPrintableString(value);
+            }
         }
         
         return new DERUTF8String(value);
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java b/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
index 19ff3f3..02ac76b 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
@@ -1,8 +1,11 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERBoolean;
 
+import java.io.IOException;
+
 /**
  * an object for the elements in the X.509 V3 extension block.
  */
@@ -61,4 +64,24 @@
         return other.getValue().equals(this.getValue())
             && (other.isCritical() == this.isCritical());
     }
+
+    /**
+     * Convert the value of the passed in extension to an object
+     * @param ext the extension to parse
+     * @return the object the value string contains
+     * @exception IllegalArgumentException if conversion is not possible
+     */
+    public static ASN1Object convertValueToObject(
+        X509Extension ext)
+        throws IllegalArgumentException
+    {
+        try
+        {
+            return ASN1Object.fromByteArray(ext.getValue().getOctets());
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("can't convert extension: " +  e);
+        }
+    }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java b/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
index be1409b..58d9504 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
@@ -1,9 +1,5 @@
 package org.bouncycastle.asn1.x509;
 
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -17,6 +13,10 @@
 import org.bouncycastle.asn1.OrderedTable;
 // END android-added
 
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
 public class X509Extensions
     extends ASN1Encodable
 {
@@ -146,6 +146,11 @@
     public static final DERObjectIdentifier SubjectInfoAccess = new DERObjectIdentifier("1.3.6.1.5.5.7.1.11");
     
     /**
+     * Logo Type
+     */
+    public static final DERObjectIdentifier LogoType = new DERObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+    /**
      * BiometricInfo
      */
     public static final DERObjectIdentifier BiometricInfo = new DERObjectIdentifier("1.3.6.1.5.5.7.1.2");
@@ -155,6 +160,21 @@
      */
     public static final DERObjectIdentifier QCStatements = new DERObjectIdentifier("1.3.6.1.5.5.7.1.3");
 
+    /**
+     * Audit identity extension in attribute certificates.
+     */
+    public static final DERObjectIdentifier AuditIdentity = new DERObjectIdentifier("1.3.6.1.5.5.7.1.4");
+    
+    /**
+     * NoRevAvail extension in attribute certificates.
+     */
+    public static final DERObjectIdentifier NoRevAvail = new DERObjectIdentifier("2.5.29.56");
+
+    /**
+     * TargetInformation extension in attribute certificates.
+     */
+    public static final DERObjectIdentifier TargetInformation = new DERObjectIdentifier("2.5.29.55");
+    
     // BEGIN android-changed
     private OrderedTable table = new OrderedTable();
     // END android-changed
@@ -327,77 +347,60 @@
      */
     public DERObject toASN1Object()
     {
+        ASN1EncodableVector     vec = new ASN1EncodableVector();
         // BEGIN android-changed
         int                     size = table.size();
-        ASN1EncodableVector     vec = new ASN1EncodableVector();
 
-        for (int i = 0; i < size; i++) {
+        for (int i = 0; i < size; i++)
+        {
             DERObjectIdentifier     oid = table.getKey(i);
             X509Extension           ext = (X509Extension)table.getValue(i);
+            // END android-changed
             ASN1EncodableVector     v = new ASN1EncodableVector();
 
             v.add(oid);
 
             if (ext.isCritical())
             {
+                // BEGIN android-changed
                 v.add(DERBoolean.TRUE);
+                // END android-changed
             }
 
             v.add(ext.getValue());
 
             vec.add(new DERSequence(v));
         }
-        // END android-changed
-        
+
         return new DERSequence(vec);
     }
 
-    public int hashCode()
+    public boolean equivalent(
+        X509Extensions other)
     {
         // BEGIN android-changed
-        int             size = table.size();
-        int             hashCode = 0;
-
-        for (int i = 0; i < size; i++) {
-            hashCode ^= table.getKey(i).hashCode();
-            hashCode ^= table.getValue(i).hashCode();
-        }
+        if (table.size() != other.table.size())
         // END android-changed
-
-        return hashCode;
-    }
-
-    public boolean equals(
-        Object o)
-    {
-        if (!(o instanceof X509Extensions))
         {
             return false;
         }
 
-        X509Extensions  other = (X509Extensions)o;
-
         // BEGIN android-changed
         Enumeration     e1 = table.getKeys();
-        Enumeration     e2 = other.table.getKeys();
         // END android-changed
 
-        while (e1.hasMoreElements() && e2.hasMoreElements())
+        while (e1.hasMoreElements())
         {
-            Object  o1 = e1.nextElement();
-            Object  o2 = e2.nextElement();
-            
-            if (!o1.equals(o2))
+            // BEGIN android-changed
+            DERObjectIdentifier  key = (DERObjectIdentifier)e1.nextElement();
+
+            if (!table.get(key).equals(other.table.get(key)))
+            // END android-changed
             {
                 return false;
             }
         }
 
-        if (e1.hasMoreElements() || e2.hasMoreElements())
-        {
-            return false;
-        }
-
         return true;
     }
 }
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java b/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
new file mode 100644
index 0000000..0487c8e
--- /dev/null
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * Generator for X.509 extensions
+ */
+public class X509ExtensionsGenerator
+{
+    private Hashtable extensions = new Hashtable();
+    private Vector extOrdering = new Vector();
+
+    /**
+     * Reset the generator
+     */
+    public void reset()
+    {
+        extensions = new Hashtable();
+        extOrdering = new Vector();
+    }
+
+    /**
+     * Add an extension with the given oid and the passed in value to be included
+     * in the OCTET STRING associated with the extension.
+     *
+     * @param oid  OID for the extension.
+     * @param critical  true if critical, false otherwise.
+     * @param value the ASN.1 object to be included in the extension.
+     */
+    public void addExtension(
+        DERObjectIdentifier oid,
+        boolean             critical,
+        DEREncodable        value)
+    {
+        try
+        {
+            this.addExtension(oid, critical, value.getDERObject().getEncoded(ASN1Encodable.DER));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error encoding value: " + e);
+        }
+    }
+
+    /**
+     * Add an extension with the given oid and the passed in byte array to be wrapped in the
+     * OCTET STRING associated with the extension.
+     *
+     * @param oid OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value the byte array to be wrapped.
+     */
+    public void addExtension(
+        DERObjectIdentifier oid,
+        boolean             critical,
+        byte[]              value)
+    {
+        if (extensions.containsKey(oid))
+        {
+            throw new IllegalArgumentException("extension " + oid + " already added");
+        }
+
+        extOrdering.addElement(oid);
+        extensions.put(oid, new X509Extension(critical, new DEROctetString(value)));
+    }
+
+    /**
+     * Return true if there are no extension present in this generator.
+     *
+     * @return true if empty, false otherwise
+     */
+    public boolean isEmpty()
+    {
+        return extOrdering.isEmpty();
+    }
+
+    /**
+     * Generate an X509Extensions object based on the current state of the generator.
+     *
+     * @return  an X09Extensions object.
+     */
+    public X509Extensions generate()
+    {
+        return new X509Extensions(extOrdering, extensions);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
index 50b1a60..ea221b6 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
@@ -1,13 +1,26 @@
 package org.bouncycastle.asn1.x509;
 
+import java.io.IOException;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Vector;
 
-import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERString;
+import org.bouncycastle.asn1.DERUniversalString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
 
 /**
  * <pre>
@@ -149,8 +162,22 @@
      * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
      * DirectoryString(SIZE(1..30))
      */
-    public static final DERObjectIdentifier POSTAL_ADDRESS = new DERObjectIdentifier(
-                    "2.5.4.16");
+    public static final DERObjectIdentifier POSTAL_ADDRESS = new DERObjectIdentifier("2.5.4.16");
+
+    /**
+     * RFC 2256 dmdName
+     */
+    public static final DERObjectIdentifier DMD_NAME = new DERObjectIdentifier("2.5.4.54");
+
+    /**
+     * id-at-telephoneNumber
+     */
+    public static final DERObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
+
+    /**
+     * id-at-name
+     */
+    public static final DERObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
 
     /**
      * Email address (RSA PKCS#9 extension) - IA5String.
@@ -180,11 +207,6 @@
     public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1");
 
     /**
-     * look up table translating OID values into their common symbols - this static is scheduled for deletion
-     */
-    public static Hashtable OIDLookUp = new Hashtable();
-
-    /**
      * determines whether or not strings should be processed and printed
      * from back to front.
      */
@@ -194,30 +216,36 @@
      * default look up table translating OID values into their common symbols following
      * the convention in RFC 2253 with a few extras
      */
-    public static Hashtable DefaultSymbols = OIDLookUp;
+    public static final Hashtable DefaultSymbols = new Hashtable();
 
     /**
      * look up table translating OID values into their common symbols following the convention in RFC 2253
      * 
      */
-    public static Hashtable RFC2253Symbols = new Hashtable();
+    public static final Hashtable RFC2253Symbols = new Hashtable();
 
     /**
      * look up table translating OID values into their common symbols following the convention in RFC 1779
      * 
      */
-    public static Hashtable RFC1779Symbols = new Hashtable();
-
-    /**
-     * look up table translating string values into their OIDS -
-     * this static is scheduled for deletion
-     */
-    public static Hashtable SymbolLookUp = new Hashtable();
+    public static final Hashtable RFC1779Symbols = new Hashtable();
 
     /**
      * look up table translating common symbols into their OIDS.
      */
-    public static Hashtable DefaultLookUp = SymbolLookUp;
+    public static final Hashtable DefaultLookUp = new Hashtable();
+
+    /**
+     * look up table translating OID values into their common symbols
+     * @deprecated use DefaultSymbols
+     */
+    public static final Hashtable OIDLookUp = DefaultSymbols;
+
+    /**
+     * look up table translating string values into their OIDS -
+     * @deprecated use DefaultLookUp
+     */
+    public static final Hashtable SymbolLookUp = DefaultLookUp;
 
     // BEGIN android-removed
     //private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
@@ -233,7 +261,7 @@
         DefaultSymbols.put(CN, "CN");
         DefaultSymbols.put(L, "L");
         DefaultSymbols.put(ST, "ST");
-        DefaultSymbols.put(SN, "SN");
+        DefaultSymbols.put(SN, "SERIALNUMBER");
         DefaultSymbols.put(EmailAddress, "E");
         DefaultSymbols.put(DC, "DC");
         DefaultSymbols.put(UID, "UID");
@@ -256,6 +284,8 @@
         DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
         DefaultSymbols.put(POSTAL_CODE, "PostalCode");
         DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
+        DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
+        DefaultSymbols.put(NAME, "Name");
 
         RFC2253Symbols.put(C, "C");
         RFC2253Symbols.put(O, "O");
@@ -307,15 +337,20 @@
         DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
         DefaultLookUp.put("postalcode", POSTAL_CODE);
         DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
+        DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
+        DefaultLookUp.put("name", NAME);
     }
 
     private X509NameEntryConverter  converter = null;
     // BEGIN android-changed
     private X509NameElementList     elems = new X509NameElementList();
     // END android-changed
-    
+
     private ASN1Sequence            seq;
 
+    private boolean                 isHashCodeCalculated;
+    private int                     hashCodeValue;
+
     /**
      * Return a X509Name based on the passed in tagged object.
      * 
@@ -342,7 +377,7 @@
             return new X509Name((ASN1Sequence)obj);
         }
 
-        throw new IllegalArgumentException("unknown object in factory \"" + obj.getClass().getName()+"\"");
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
     /**
@@ -359,32 +394,40 @@
 
         while (e.hasMoreElements())
         {
-            ASN1Set         set = (ASN1Set)e.nextElement();
+            ASN1Set         set = ASN1Set.getInstance(e.nextElement());
 
             for (int i = 0; i < set.size(); i++) 
             {
+                   ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i));
+
+                   if (s.size() != 2)
+                   {
+                       throw new IllegalArgumentException("badly sized pair");
+                   }
+
                    // BEGIN android-changed
-                   ASN1Sequence s = (ASN1Sequence)set.getObjectAt(i);
+                   DERObjectIdentifier key = DERObjectIdentifier.getInstance(s.getObjectAt(0));
                    
-                   DERObjectIdentifier key =
-                       (DERObjectIdentifier) s.getObjectAt(0);
                    DEREncodable value = s.getObjectAt(1);
                    String valueStr;
-
-                   if (value instanceof DERString)
+                   if (value instanceof DERString && !(value instanceof DERUniversalString))
                    {
-                       valueStr = ((DERString)value).getString();
+                       String v = ((DERString)value).getString();
+                       if (v.length() > 0 && v.charAt(0) == '#')
+                       {
+                           valueStr = "\\" + v;
+                       }
+                       else
+                       {
+                           valueStr = v;
+                       }
                    }
                    else
                    {
                        valueStr = "#" + bytesToString(Hex.encode(value.getDERObject().getDEREncoded()));
                    }
-
-                   /*
-                    * The added flag set to (i != 0), to allow earlier JDK
-                    * compatibility.
-                    */
-                   elems.add(key, valueStr, i != 0);
+                   boolean added = (i != 0);  // to allow earlier JDK compatibility
+                   elems.add(key, valueStr, added);
                    // END android-changed
             }
         }
@@ -400,6 +443,7 @@
      * <b>Note:</b> if the name you are trying to generate should be
      * following a specific ordering, you should use the constructor
      * with the ordering specified below.
+     * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
      */
     public X509Name(
         Hashtable  attributes)
@@ -434,9 +478,9 @@
      * ASN.1 counterparts.
      */
     public X509Name(
-        Vector                      ordering,
-        Hashtable                   attributes,
-        X509DefaultEntryConverter   converter)
+        Vector                   ordering,
+        Hashtable                attributes,
+        X509NameEntryConverter   converter)
     {
         // BEGIN android-changed
         DERObjectIdentifier problem = null;
@@ -519,6 +563,16 @@
         }
     }
 
+//    private Boolean isEncoded(String s)
+//    {
+//        if (s.charAt(0) == '#')
+//        {
+//            return TRUE;
+//        }
+//
+//        return FALSE;
+//    }
+
     /**
      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
      * some such, converting it into an ordered set of name attributes.
@@ -652,9 +706,10 @@
             if (value.indexOf('+') > 0)
             {
                 X509NameTokenizer   vTok = new X509NameTokenizer(value, '+');
+                String  v = vTok.nextToken();
 
                 // BEGIN android-changed
-                elems.add(oid, vTok.nextToken());
+                this.elems.add(oid, v);
                 // END android-changed
 
                 while (vTok.hasMoreTokens())
@@ -665,14 +720,14 @@
                     String  nm = sv.substring(0, ndx);
                     String  vl = sv.substring(ndx + 1);
                     // BEGIN android-changed
-                    elems.add(decodeOID(nm, lookUp), vl, true);
+                    this.elems.add(decodeOID(nm, lookUp), vl, true);
                     // END android-changed
                 }
             }
             else
             {
                 // BEGIN android-changed
-                elems.add(oid, value);
+                this.elems.add(oid, value);
                 // END android-changed
             }
         }
@@ -680,7 +735,7 @@
         if (reverse)
         {
             // BEGIN android-changed
-            elems = elems.reverse();
+            this.elems = this.elems.reverse();
             // END android-changed
         }
     }
@@ -709,27 +764,59 @@
      */
     public Vector getValues()
     {
-        // BEGIN android-changed
         Vector  v = new Vector();
+        // BEGIN android-changed
         int     size = elems.size();
 
-        for (int i = 0; i < size; i++)
+        for (int i = 0; i != size; i++)
         {
             v.addElement(elems.getValue(i));
         }
+        // END android-changed
 
         return v;
+    }
+
+    /**
+     * return a vector of the values found in the name, in the order they
+     * were found, with the DN label corresponding to passed in oid.
+     */
+    public Vector getValues(
+        DERObjectIdentifier oid)
+    {
+        Vector  v = new Vector();
+        int     size = elems.size();
+        // BEGIN android-changed
+
+        for (int i = 0; i != size; i++)
+        {
+            if (elems.getKey(i).equals(oid))
+            {
+                String val = elems.getValue(i);
+
+                if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
+                {
+                    v.addElement(val.substring(1));
+                }
+                else
+                {
+                    v.addElement(val);
+                }
+            }
+        }
         // END android-changed
+
+        return v;
     }
 
     public DERObject toASN1Object()
     {
         if (seq == null)
         {
-            // BEGIN android-changed
             ASN1EncodableVector  vec = new ASN1EncodableVector();
             ASN1EncodableVector  sVec = new ASN1EncodableVector();
             DERObjectIdentifier  lstOid = null;
+            // BEGIN android-changed
             int                  size = elems.size();
             
             for (int i = 0; i != size; i++)
@@ -742,8 +829,10 @@
                 String  str = elems.getValue(i);
 
                 v.add(converter.getConvertedValue(oid, str));
-
-                if (lstOid == null || elems.getAdded(i))
+ 
+                if (lstOid == null 
+                    || this.elems.getAdded(i))
+                // END android-changed
                 {
                     sVec.add(new DERSequence(v));
                 }
@@ -771,94 +860,67 @@
      * @param inOrder if true the order of both X509 names must be the same,
      * as well as the values associated with each element.
      */
-    public boolean equals(Object _obj, boolean inOrder) 
+    public boolean equals(Object obj, boolean inOrder)
     {
-        if (_obj == this)
+        if (!inOrder)
+        {
+            return this.equals(obj);
+        }
+
+        if (obj == this)
         {
             return true;
         }
 
-        if (!inOrder)
-        {
-            return this.equals(_obj);
-        }
-
-        if (!(_obj instanceof X509Name))
+        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
         {
             return false;
         }
-        
-        X509Name _oxn          = (X509Name)_obj;
+
+        DERObject derO = ((DEREncodable)obj).getDERObject();
+
+        if (this.getDERObject().equals(derO))
+        {
+            return true;
+        }
+
+        X509Name other;
+
+        try
+        {
+            other = X509Name.getInstance(obj);
+        }
+        catch (IllegalArgumentException e)
+        {
+            return false;
+        }
+
         // BEGIN android-changed
-        int      _orderingSize = elems.size();
+        int      orderingSize = elems.size();
 
-        if (_orderingSize != _oxn.elems.size()) 
+        if (orderingSize != other.elems.size())
+        // END android-changed
         {
             return false;
         }
-        // END android-changed
-        
-        for(int i = 0; i < _orderingSize; i++) 
+
+        for (int i = 0; i < orderingSize; i++)
         {
             // BEGIN android-changed
-            String  _oid   = elems.getKey(i).getId();
-            String  _val   = elems.getValue(i);
-            
-            String _oOID = _oxn.elems.getKey(i).getId();
-            String _oVal = _oxn.elems.getValue(i);
-            // BEGIN android-changed
+            DERObjectIdentifier  oid = elems.getKey(i);
+            DERObjectIdentifier  oOid = other.elems.getKey(i);
+            // END android-changed
 
-            if (_oid.equals(_oOID))
+            if (oid.equals(oOid))
             {
-                _val = Strings.toLowerCase(_val.trim());
-                _oVal = Strings.toLowerCase(_oVal.trim());
-                if (_val.equals(_oVal))
+                // BEGIN android-changed
+                String value = elems.getValue(i);
+                String oValue = other.elems.getValue(i);
+                // END android-changed
+
+                if (!equivalentStrings(value, oValue))
                 {
-                    continue;
-                }
-                else
-                {
-                    StringBuffer    v1 = new StringBuffer();
-                    StringBuffer    v2 = new StringBuffer();
-
-                    if (_val.length() != 0)
-                    {
-                        char    c1 = _val.charAt(0);
-
-                        v1.append(c1);
-
-                        for (int k = 1; k < _val.length(); k++)
-                        {
-                            char    c2 = _val.charAt(k);
-                            if (!(c1 == ' ' && c2 == ' '))
-                            {
-                                v1.append(c2);
-                            }
-                            c1 = c2;
-                        }
-                    }
-
-                    if (_oVal.length() != 0)
-                    {
-                        char    c1 = _oVal.charAt(0);
-
-                        v2.append(c1);
-
-                        for (int k = 1; k < _oVal.length(); k++)
-                        {
-                            char    c2 = _oVal.charAt(k);
-                            if (!(c1 == ' ' && c2 == ' '))
-                            {
-                                v2.append(c2);
-                            }
-                            c1 = c2;
-                        }
-                    }
-
-                    if (!v1.toString().equals(v2.toString()))
-                    {
-                        return false;
-                    }
+                    return false;
                 }
             }
             else
@@ -870,126 +932,124 @@
         return true;
     }
 
+    public int hashCode()
+    {
+        if (isHashCodeCalculated)
+        {
+            return hashCodeValue;
+        }
+
+        isHashCodeCalculated = true;
+
+        // this needs to be order independent, like equals
+        for (int i = 0; i != elems.size(); i += 1)
+        {
+            String value = (String)elems.getValue(i);
+
+            value = canonicalize(value);
+            value = stripInternalSpaces(value);
+
+            hashCodeValue ^= value.hashCode();
+        }
+
+        return hashCodeValue;
+    }
+
     /**
      * test for equality - note: case is ignored.
      */
-    public boolean equals(Object _obj) 
+    public boolean equals(Object obj)
     {
-        if (_obj == this)
+        if (obj == this)
         {
             return true;
         }
 
-        if (!(_obj instanceof X509Name || _obj instanceof ASN1Sequence))
+        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
         {
             return false;
         }
         
-        DERObject derO = ((DEREncodable)_obj).getDERObject();
+        DERObject derO = ((DEREncodable)obj).getDERObject();
         
         if (this.getDERObject().equals(derO))
         {
             return true;
         }
-        
-        if (!(_obj instanceof X509Name))
+
+        X509Name other;
+
+        try
         {
+            other = X509Name.getInstance(obj);
+        }
+        catch (IllegalArgumentException e)
+        { 
             return false;
         }
-        
-        X509Name _oxn          = (X509Name)_obj;
 
         // BEGIN android-changed
-        int      _orderingSize = elems.size();
+        int      orderingSize = elems.size();
 
-        if (_orderingSize != _oxn.elems.size()) 
+        if (orderingSize != other.elems.size())
+            // END android-changed
         {
             return false;
         }
-        // END android-changed
         
-        boolean[] _indexes = new boolean[_orderingSize];
+        boolean[] indexes = new boolean[orderingSize];
+        int       start, end, delta;
 
-        for(int i = 0; i < _orderingSize; i++) 
+        // BEGIN android-changed
+        if (elems.getKey(0).equals(other.elems.getKey(0)))   // guess forward
+        // END android-changed
         {
-            boolean _found = false;
+            start = 0;
+            end = orderingSize;
+            delta = 1;
+        }
+        else  // guess reversed - most common problem
+        {
+            start = orderingSize - 1;
+            end = -1;
+            delta = -1;
+        }
+
+        for (int i = start; i != end; i += delta)
+        {
+            boolean              found = false;
             // BEGIN android-changed
-            String  _oid   = elems.getKey(i).getId();
-            String  _val   = elems.getValue(i);
+            DERObjectIdentifier  oid = elems.getKey(i);
+            String               value = elems.getValue(i);
             // END android-changed
-            
-            for(int j = 0; j < _orderingSize; j++) 
+
+            for (int j = 0; j < orderingSize; j++)
             {
-                if (_indexes[j])
+                if (indexes[j])
                 {
                     continue;
                 }
 
                 // BEGIN android-changed
-                String _oOID = elems.getKey(j).getId();
-                String _oVal = _oxn.elems.getValue(j);
+                DERObjectIdentifier oOid = other.elems.getKey(j);
                 // END android-changed
 
-                if (_oid.equals(_oOID))
+                if (oid.equals(oOid))
                 {
-                    _val = Strings.toLowerCase(_val.trim());
-                    _oVal = Strings.toLowerCase(_oVal.trim());
-                    if (_val.equals(_oVal))
+                    // BEGIN android-changed
+                    String oValue = other.elems.getValue(j);
+                    // END android-changed
+
+                    if (equivalentStrings(value, oValue))
                     {
-                        _indexes[j] = true;
-                        _found      = true;
+                        indexes[j] = true;
+                        found      = true;
                         break;
                     }
-                    else
-                    {
-                        StringBuffer    v1 = new StringBuffer();
-                        StringBuffer    v2 = new StringBuffer();
-
-                        if (_val.length() != 0)
-                        {
-                            char    c1 = _val.charAt(0);
-
-                            v1.append(c1);
-
-                            for (int k = 1; k < _val.length(); k++)
-                            {
-                                char    c2 = _val.charAt(k);
-                                if (!(c1 == ' ' && c2 == ' '))
-                                {
-                                    v1.append(c2);
-                                }
-                                c1 = c2;
-                            }
-                        }
-
-                        if (_oVal.length() != 0)
-                        {
-                            char    c1 = _oVal.charAt(0);
-
-                            v2.append(c1);
-
-                            for (int k = 1; k < _oVal.length(); k++)
-                            {
-                                char    c2 = _oVal.charAt(k);
-                                if (!(c1 == ' ' && c2 == ' '))
-                                {
-                                    v2.append(c2);
-                                }
-                                c1 = c2;
-                            }
-                        }
-
-                        if (v1.toString().equals(v2.toString()))
-                        {
-                            _indexes[j] = true;
-                            _found      = true;
-                            break;
-                        }
-                    }
                 }
             }
 
-            if(!_found)
+            if (!found)
             {
                 return false;
             }
@@ -997,19 +1057,78 @@
         
         return true;
     }
-    
-    public int hashCode()
-    {
-        ASN1Sequence  seq = (ASN1Sequence)this.getDERObject();
-        Enumeration   e = seq.getObjects();
-        int           hashCode = 0;
 
-        while (e.hasMoreElements())
+    private boolean equivalentStrings(String s1, String s2)
+    {
+        String value = canonicalize(s1);
+        String oValue = canonicalize(s2);
+        
+        if (!value.equals(oValue))
         {
-            hashCode ^= e.nextElement().hashCode();
+            value = stripInternalSpaces(value);
+            oValue = stripInternalSpaces(oValue);
+
+            if (!value.equals(oValue))
+            {
+                return false;
+            }
         }
 
-        return hashCode;
+        return true;
+    }
+
+    private String canonicalize(String s)
+    {
+        String value = Strings.toLowerCase(s.trim());
+        
+        if (value.length() > 0 && value.charAt(0) == '#')
+        {
+            DERObject obj = decodeObject(value);
+
+            if (obj instanceof DERString)
+            {
+                value = Strings.toLowerCase(((DERString)obj).getString().trim());
+            }
+        }
+
+        return value;
+    }
+
+    private ASN1Object decodeObject(String oValue)
+    {
+        try
+        {
+            return ASN1Object.fromByteArray(Hex.decode(oValue.substring(1)));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("unknown encoding in name: " + e);
+        }
+    }
+
+    private String stripInternalSpaces(
+        String str)
+    {
+        StringBuffer res = new StringBuffer();
+
+        if (str.length() != 0)
+        {
+            char    c1 = str.charAt(0);
+
+            res.append(c1);
+
+            for (int k = 1; k < str.length(); k++)
+            {
+                char    c2 = str.charAt(k);
+                if (!(c1 == ' ' && c2 == ' '))
+                {
+                    res.append(c2);
+                }
+                c1 = c2;
+            }
+        }
+
+        return res.toString();
     }
 
     private void appendValue(
@@ -1032,17 +1151,23 @@
         buf.append('=');
 
         int     index = buf.length();
-
+        
         buf.append(value);
 
         int     end = buf.length();
 
+        if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
+        {
+            index += 2;   
+        }
+
         while (index != end)
         {
             if ((buf.charAt(index) == ',')
                || (buf.charAt(index) == '"')
                || (buf.charAt(index) == '\\')
                || (buf.charAt(index) == '+')
+               || (buf.charAt(index) == '=')
                || (buf.charAt(index) == '<')
                || (buf.charAt(index) == '>')
                || (buf.charAt(index) == ';'))
@@ -1073,12 +1198,41 @@
         Hashtable   oidSymbols)
     {
         StringBuffer            buf = new StringBuffer();
+        Vector                  components = new Vector();
         boolean                 first = true;
 
+        StringBuffer ava = null;
+
+        // BEGIN android-changed
+        for (int i = 0; i < elems.size(); i++)
+        // END android-changed
+        {
+            if (elems.getAdded(i))
+            {
+                ava.append('+');
+                appendValue(ava, oidSymbols,
+                    // BEGIN android-changed
+                    elems.getKey(i),
+                    elems.getValue(i));
+                    // END android-changed
+            }
+            else
+            {
+                ava = new StringBuffer();
+                appendValue(ava, oidSymbols,
+                    // BEGIN android-changed
+                    elems.getKey(i),
+                    elems.getValue(i));
+                    // END android-changed
+                components.addElement(ava);
+            }
+        }
+
         if (reverse)
         {
             // BEGIN android-changed
             for (int i = elems.size() - 1; i >= 0; i--)
+            // END android-changed
             {
                 if (first)
                 {
@@ -1086,26 +1240,15 @@
                 }
                 else
                 {
-                    if (elems.getAdded(i + 1))
-                    {
-                        buf.append('+');
-                    }
-                    else
-                    {
-                        buf.append(',');
-                    }
+                    buf.append(',');
                 }
 
-                appendValue(buf, oidSymbols, 
-                            elems.getKey(i),
-                            elems.getValue(i));
+                buf.append(components.elementAt(i).toString());
             }
-            // END android-changed
         }
         else
         {
-            // BEGIN android-changed
-            for (int i = 0; i < elems.size(); i++)
+            for (int i = 0; i < components.size(); i++)
             {
                 if (first)
                 {
@@ -1113,21 +1256,11 @@
                 }
                 else
                 {
-                    if (elems.getAdded(i))
-                    {
-                        buf.append('+');
-                    }
-                    else
-                    {
-                        buf.append(',');
-                    }
+                    buf.append(',');
                 }
 
-                appendValue(buf, oidSymbols, 
-                            elems.getKey(i),
-                            elems.getValue(i));
+                buf.append(components.elementAt(i).toString());
             }
-            // END android-changed
         }
 
         return buf.toString();
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
index 24075f7..5011322 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
@@ -1,12 +1,13 @@
 package org.bouncycastle.asn1.x509;
 
-import java.io.IOException;
-
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERPrintableString;
 import org.bouncycastle.util.Strings;
 
+import java.io.IOException;
+
 /**
  * It turns out that the number of standard ways the fields in a DN should be 
  * encoded into their ASN.1 counterparts is rapidly approaching the
@@ -97,49 +98,7 @@
     protected boolean canBePrintable(
         String  str)
     {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-            
-            if (str.charAt(i) > 0x007f)
-            {
-                return false;
-            }
-            
-            if ('a' <= ch && ch <= 'z')
-            {
-                continue;
-            }
-            
-            if ('A' <= ch && ch <= 'Z')
-            {
-                continue;
-            }
-            
-            if ('0' <= ch && ch <= '9')
-            {
-                continue;
-            }
-            
-            switch (ch)
-            {
-            case ' ':
-            case '\'':
-            case '(':
-            case ')':
-            case '+':
-            case '-':
-            case '.':
-            case ':':
-            case '=':
-            case '?':
-                continue;
-            }
-            
-            return false;
-        }
-
-        return true;
+        return DERPrintableString.isPrintableString(str);
     }
     
     /**
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
index 8f0d08b..440e147 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
@@ -58,14 +58,6 @@
                 }
                 else
                 {
-                    buf.append(c);
-                }
-                escaped = false;
-            }
-            else
-            {
-                if (escaped || quoted)
-                {
                     // BEGIN android-added
                     // copied from a newer version of BouncyCastle
                     if (c == '#' && buf.charAt(buf.length() - 1) == '=')
@@ -78,6 +70,22 @@
                     }
                     // END android-added
                     buf.append(c);
+                }
+                escaped = false;
+            }
+            else
+            {
+                if (escaped || quoted)
+                {
+                    if (c == '#' && buf.charAt(buf.length() - 1) == '=')
+                    {
+                        buf.append('\\');
+                    }
+                    else if (c == '+' && seperator != '+')
+                    {
+                        buf.append('\\');
+                    }
+                    buf.append(c);
                     escaped = false;
                 }
                 else if (c == '\\')
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index 1f8b8a2..cbf7b76 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -16,6 +16,9 @@
     static final DERObjectIdentifier    organization            = new DERObjectIdentifier(id + ".10");
     static final DERObjectIdentifier    organizationalUnitName  = new DERObjectIdentifier(id + ".11");
 
+    static final DERObjectIdentifier    id_at_telephoneNumber   = new DERObjectIdentifier("2.5.4.20");
+    static final DERObjectIdentifier    id_at_name              = new DERObjectIdentifier(id + ".41");
+
     // id-SHA1 OBJECT IDENTIFIER ::=    
     //   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }    //
     static final DERObjectIdentifier    id_SHA1                 = new DERObjectIdentifier("1.3.14.3.2.26");
@@ -35,9 +38,25 @@
 
     static final DERObjectIdentifier    id_ea_rsa = new DERObjectIdentifier("2.5.8.1.1");
     
+    // id-pkix
+    static final DERObjectIdentifier id_pkix = new DERObjectIdentifier("1.3.6.1.5.5.7");
+
     //
-    //    OID for ocsp uri in AuthorityInformationAccess extension
+    // private internet extensions
     //
-     static final DERObjectIdentifier ocspAccessMethod = new DERObjectIdentifier("1.3.6.1.5.5.7.48.1");
+    static final DERObjectIdentifier  id_pe = new DERObjectIdentifier(id_pkix + ".1");
+
+    //
+    // authority information access
+    //
+    static final DERObjectIdentifier  id_ad = new DERObjectIdentifier(id_pkix + ".48");
+    static final DERObjectIdentifier  id_ad_caIssuers = new DERObjectIdentifier(id_ad + ".2");
+    static final DERObjectIdentifier  id_ad_ocsp = new DERObjectIdentifier(id_ad + ".1");
+
+    //
+    //    OID for ocsp and crl uri in AuthorityInformationAccess extension
+    //
+    static final DERObjectIdentifier ocspAccessMethod = id_ad_ocsp;
+    static final DERObjectIdentifier crlAccessMethod = id_ad_caIssuers;
 }
 
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java
deleted file mode 100644
index 9f373fa..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/BiometricData.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-
-/**
- * The BiometricData object.
- * <pre>
- * BiometricData  ::=  SEQUENCE {
- *       typeOfBiometricData  TypeOfBiometricData,
- *       hashAlgorithm        AlgorithmIdentifier,
- *       biometricDataHash    OCTET STRING,
- *       sourceDataUri        IA5String OPTIONAL  }
- * </pre>
- */
-public class BiometricData 
-    extends ASN1Encodable
-{
-    TypeOfBiometricData typeOfBiometricData;
-    AlgorithmIdentifier hashAlgorithm;
-    ASN1OctetString     biometricDataHash;
-    DERIA5String        sourceDataUri;    
-    
-    public static BiometricData getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof BiometricData)
-        {
-            return (BiometricData)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new BiometricData(ASN1Sequence.getInstance(obj));            
-        }
-        else
-        {
-            throw new IllegalArgumentException("unknown object in getInstance");
-        }
-    }                
-            
-    public BiometricData(ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        // typeOfBiometricData
-        typeOfBiometricData = TypeOfBiometricData.getInstance(e.nextElement());
-        // hashAlgorithm
-        hashAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
-        // biometricDataHash
-        biometricDataHash = ASN1OctetString.getInstance(e.nextElement());
-        // sourceDataUri
-        if (e.hasMoreElements())
-        {
-            sourceDataUri = DERIA5String.getInstance(e.nextElement());
-        }
-    }
-    
-    public BiometricData(
-        TypeOfBiometricData typeOfBiometricData,
-        AlgorithmIdentifier hashAlgorithm,
-        ASN1OctetString     biometricDataHash,
-        DERIA5String        sourceDataUri)
-    {
-        this.typeOfBiometricData = typeOfBiometricData;
-        this.hashAlgorithm = hashAlgorithm;
-        this.biometricDataHash = biometricDataHash;
-        this.sourceDataUri = sourceDataUri;
-    }
-    
-    public BiometricData(
-        TypeOfBiometricData typeOfBiometricData,
-        AlgorithmIdentifier hashAlgorithm,
-        ASN1OctetString     biometricDataHash)
-    {
-        this.typeOfBiometricData = typeOfBiometricData;
-        this.hashAlgorithm = hashAlgorithm;
-        this.biometricDataHash = biometricDataHash;
-        this.sourceDataUri = null;
-    }
-
-    public TypeOfBiometricData getTypeOfBiometricData()
-    {
-        return typeOfBiometricData;
-    }
-    
-    public AlgorithmIdentifier getHashAlgorithm()
-    {
-        return hashAlgorithm;
-    }
-    
-    public ASN1OctetString getBiometricDataHash()
-    {
-        return biometricDataHash;
-    }
-    
-    public DERIA5String getSourceDataUri()
-    {
-        return sourceDataUri;
-    }
-    
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        seq.add(typeOfBiometricData);
-        seq.add(hashAlgorithm);
-        seq.add(biometricDataHash); 
-        
-        if (sourceDataUri != null)
-        {
-            seq.add(sourceDataUri);
-        }
-
-        return new DERSequence(seq);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
deleted file mode 100644
index eef97e3..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public interface ETSIQCObjectIdentifiers
-{
-    //
-    // base id
-    //
-    static final String                 id_etsi_qcs                  = "0.4.0.1862.1";
-
-    static final DERObjectIdentifier    id_etsi_qcs_QcCompliance     = new DERObjectIdentifier(id_etsi_qcs+".1");
-    static final DERObjectIdentifier    id_etsi_qcs_LimiteValue      = new DERObjectIdentifier(id_etsi_qcs+".2");
-    static final DERObjectIdentifier    id_etsi_qcs_RetentionPeriod  = new DERObjectIdentifier(id_etsi_qcs+".3");
-    static final DERObjectIdentifier    id_etsi_qcs_QcSSCD           = new DERObjectIdentifier(id_etsi_qcs+".4");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
deleted file mode 100644
index 10ced50..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import org.bouncycastle.asn1.ASN1Choice;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERPrintableString;
-
-/**
- * The Iso4217CurrencyCode object.
- * <pre>
- * Iso4217CurrencyCode  ::=  CHOICE {
- *       alphabetic              PrintableString (SIZE 3), --Recommended
- *       numeric              INTEGER (1..999) }
- * -- Alphabetic or numeric currency code as defined in ISO 4217
- * -- It is recommended that the Alphabetic form is used
- * </pre>
- */
-public class Iso4217CurrencyCode 
-    extends ASN1Encodable
-    implements ASN1Choice
-{
-    final int ALPHABETIC_MAXSIZE = 3;
-    final int NUMERIC_MINSIZE = 1;
-    final int NUMERIC_MAXSIZE = 999;
-    
-    DEREncodable obj;    
-    int          numeric;
-    
-    public static Iso4217CurrencyCode getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof Iso4217CurrencyCode)
-        {
-            return (Iso4217CurrencyCode)obj;
-        }
-
-        if (obj instanceof DERInteger)
-        {
-            DERInteger numericobj = DERInteger.getInstance(obj);
-            int numeric = numericobj.getValue().intValue();  
-            return new Iso4217CurrencyCode(numeric);            
-        }
-        else
-        if (obj instanceof DERPrintableString)
-        {
-            DERPrintableString alphabetic = DERPrintableString.getInstance(obj);
-            return new Iso4217CurrencyCode(alphabetic.getString());
-        }
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }
-            
-    public Iso4217CurrencyCode(
-        int numeric)
-    {
-        if (numeric > NUMERIC_MAXSIZE || numeric < NUMERIC_MINSIZE)
-        {
-            throw new IllegalArgumentException("wrong size in numeric code : not in (" +NUMERIC_MINSIZE +".."+ NUMERIC_MAXSIZE +")");
-        }
-        obj = new DERInteger(numeric);
-    }
-    
-    public Iso4217CurrencyCode(
-        String alphabetic)
-    {
-        if (alphabetic.length() > ALPHABETIC_MAXSIZE)
-        {
-            throw new IllegalArgumentException("wrong size in alphabetic code : max size is " + ALPHABETIC_MAXSIZE);
-        }
-        obj = new DERPrintableString(alphabetic);
-    }            
-
-    public boolean isAlphabetic()
-    {
-        return obj instanceof DERPrintableString;
-    }
-    
-    public String getAlphabetic()
-    {
-        return ((DERPrintableString)obj).getString();
-    }
-    
-    public int getNumeric()
-    {
-        return ((DERInteger)obj).getValue().intValue();
-    }
-    
-    public DERObject toASN1Object() 
-    {    
-        return obj.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java
deleted file mode 100644
index c8c0ce3..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/MonetaryValue.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import java.math.BigInteger;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The MonetaryValue object.
- * <pre>
- * MonetaryValue  ::=  SEQUENCE {
- *       currency              Iso4217CurrencyCode,
- *       amount               INTEGER, 
- *       exponent             INTEGER }
- * -- value = amount * 10^exponent
- * </pre>
- */
-public class MonetaryValue 
-    extends ASN1Encodable
-{
-    Iso4217CurrencyCode currency;
-    DERInteger          amount;
-    DERInteger          exponent;
-        
-    public static MonetaryValue getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof MonetaryValue)
-        {
-            return (MonetaryValue)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new MonetaryValue(ASN1Sequence.getInstance(obj));            
-        }
-        
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }
-        
-    public MonetaryValue(
-        ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();    
-        // currency
-        currency = Iso4217CurrencyCode.getInstance(e.nextElement());
-        // hashAlgorithm
-        amount = DERInteger.getInstance(e.nextElement());
-        // exponent
-        exponent = DERInteger.getInstance(e.nextElement());            
-    }
-        
-    public MonetaryValue(
-        Iso4217CurrencyCode currency, 
-        int                 amount, 
-        int                 exponent)
-    {    
-        this.currency = currency;
-        this.amount = new DERInteger(amount);
-        this.exponent = new DERInteger(exponent);                  
-    }                    
-             
-    public Iso4217CurrencyCode getCurrency()
-    {
-        return currency;
-    }
-        
-    public BigInteger getAmount()
-    {
-        return amount.getValue();
-    }
-        
-    public BigInteger getExponent()
-    {
-        return exponent.getValue();
-    }   
-    
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        seq.add(currency);
-        seq.add(amount);
-        seq.add(exponent); 
-        
-        return new DERSequence(seq);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java
deleted file mode 100644
index 6b87ea0..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/QCStatement.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-
-/**
- * The QCStatement object.
- * <pre>
- * QCStatement ::= SEQUENCE {
- *   statementId        OBJECT IDENTIFIER,
- *   statementInfo      ANY DEFINED BY statementId OPTIONAL} 
- * </pre>
- */
-
-public class QCStatement 
-    extends ASN1Encodable 
-    implements ETSIQCObjectIdentifiers, RFC3739QCObjectIdentifiers
-{
-    DERObjectIdentifier qcStatementId;
-    ASN1Encodable       qcStatementInfo;
-
-    public static QCStatement getInstance(
-        Object obj)
-    {
-        if (obj == null || obj instanceof QCStatement)
-        {
-            return (QCStatement)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new QCStatement(ASN1Sequence.getInstance(obj));            
-        }
-        
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }    
-    
-    public QCStatement(
-        ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-
-        // qcStatementId
-        qcStatementId = DERObjectIdentifier.getInstance(e.nextElement());
-        // qcstatementInfo
-        if (e.hasMoreElements())
-        {
-            qcStatementInfo = (ASN1Encodable) e.nextElement();
-        }
-    }    
-    
-    public QCStatement(
-        DERObjectIdentifier qcStatementId)
-    {
-        this.qcStatementId = qcStatementId;
-        this.qcStatementInfo = null;
-    }
-    
-    public QCStatement(
-        DERObjectIdentifier qcStatementId, 
-        ASN1Encodable       qcStatementInfo)
-    {
-        this.qcStatementId = qcStatementId;
-        this.qcStatementInfo = qcStatementInfo;
-    }    
-        
-    public DERObjectIdentifier getStatementId()
-    {
-        return qcStatementId;
-    }
-    
-    public ASN1Encodable getStatementInfo()
-    {
-        return qcStatementInfo;
-    }
-
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        seq.add(qcStatementId);       
-        
-        if (qcStatementInfo != null)
-        {
-            seq.add(qcStatementInfo);
-        }
-
-        return new DERSequence(seq);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
deleted file mode 100644
index 8762f2f..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-public interface RFC3739QCObjectIdentifiers
-{
-    //
-    // base id
-    //
-    static final String                 id_qcs             = "1.3.6.1.5.5.7.11";
-
-    static final DERObjectIdentifier    id_qcs_pkixQCSyntax_v1                = new DERObjectIdentifier(id_qcs+".1");
-    static final DERObjectIdentifier    id_qcs_pkixQCSyntax_v2                 = new DERObjectIdentifier(id_qcs+".2");
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java
deleted file mode 100644
index 445e8b2..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/SemanticsInformation.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.GeneralName;
-
-/**
- * The SemanticsInformation object.
- * <pre>
- *       SemanticsInformation ::= SEQUENCE {
- *         semanticsIdentifier        OBJECT IDENTIFIER   OPTIONAL,
- *         nameRegistrationAuthorities NameRegistrationAuthorities
- *                                                         OPTIONAL }
- *         (WITH COMPONENTS {..., semanticsIdentifier PRESENT}|
- *          WITH COMPONENTS {..., nameRegistrationAuthorities PRESENT})
- *
- *     NameRegistrationAuthorities ::=  SEQUENCE SIZE (1..MAX) OF
- *         GeneralName
- * </pre>
- */
-public class SemanticsInformation extends ASN1Encodable
-{
-    DERObjectIdentifier semanticsIdentifier;
-    GeneralName[] nameRegistrationAuthorities;    
-    
-    public static SemanticsInformation getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof SemanticsInformation)
-        {
-            return (SemanticsInformation)obj;
-        }
-
-        if (obj instanceof ASN1Sequence)
-        {
-            return new SemanticsInformation(ASN1Sequence.getInstance(obj));            
-        }
-        
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }
-        
-    public SemanticsInformation(ASN1Sequence seq)
-    {
-        Enumeration e = seq.getObjects();
-        if (seq.size() < 1)
-        {
-             throw new IllegalArgumentException("no objects in SemanticsInformation");
-        }
-        
-        Object object = e.nextElement();
-        if (object instanceof DERObjectIdentifier)
-        {
-            semanticsIdentifier = DERObjectIdentifier.getInstance(object);
-            if (e.hasMoreElements())
-            {
-                object = e.nextElement();
-            }
-            else
-            {
-                object = null;
-            }
-        }
-        
-        if (object != null)
-        {
-            ASN1Sequence generalNameSeq = ASN1Sequence.getInstance(object);
-            nameRegistrationAuthorities = new GeneralName[generalNameSeq.size()];
-            for (int i= 0; i < generalNameSeq.size(); i++)
-            {
-                nameRegistrationAuthorities[i] = GeneralName.getInstance(generalNameSeq.getObjectAt(i));
-            } 
-        }
-    }
-        
-    public SemanticsInformation(
-        DERObjectIdentifier semanticsIdentifier,
-        GeneralName[] generalNames)
-    {
-        this.semanticsIdentifier = semanticsIdentifier;
-        this.nameRegistrationAuthorities = generalNames;
-    }
-
-    public SemanticsInformation(DERObjectIdentifier semanticsIdentifier)
-    {
-        this.semanticsIdentifier = semanticsIdentifier;
-        this.nameRegistrationAuthorities = null;
-    }
-
-    public SemanticsInformation(GeneralName[] generalNames)
-    {
-        this.semanticsIdentifier = null;
-        this.nameRegistrationAuthorities = generalNames;
-    }        
-    
-    public DERObjectIdentifier getSemanticsIdentifier()
-    {
-        return semanticsIdentifier;
-    }
-        
-    public GeneralName[] getNameRegistrationAuthorities()
-    {
-        return nameRegistrationAuthorities;
-    } 
-    
-    public DERObject toASN1Object() 
-    {
-        ASN1EncodableVector seq = new ASN1EncodableVector();
-        
-        if (this.semanticsIdentifier != null)
-        {
-            seq.add(semanticsIdentifier);
-        }
-        if (this.nameRegistrationAuthorities != null)
-        {
-            ASN1EncodableVector seqname = new ASN1EncodableVector();
-            for (int i = 0; i < nameRegistrationAuthorities.length; i++) 
-            {
-                seqname.add(nameRegistrationAuthorities[i]);
-            }            
-            seq.add(new DERSequence(seqname));
-        }            
-        
-        return new DERSequence(seq);
-    }                   
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java b/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java
deleted file mode 100644
index 3ab384a..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/qualified/TypeOfBiometricData.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.bouncycastle.asn1.x509.qualified;
-
-import org.bouncycastle.asn1.ASN1Choice;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-/**
- * The TypeOfBiometricData object.
- * <pre>
- * TypeOfBiometricData ::= CHOICE {
- *   predefinedBiometricType   PredefinedBiometricType,
- *   biometricDataOid          OBJECT IDENTIFIER }
- *
- * PredefinedBiometricType ::= INTEGER {
- *   picture(0),handwritten-signature(1)}
- *   (picture|handwritten-signature)
- * </pre>
- */
-public class TypeOfBiometricData  
-    extends ASN1Encodable
-    implements ASN1Choice
-{
-    public static final int PICTURE                     = 0;
-    public static final int HANDWRITTEN_SIGNATURE       = 1;
-
-    DEREncodable      obj;
-
-    public static TypeOfBiometricData getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof TypeOfBiometricData)
-        {
-            return (TypeOfBiometricData)obj;
-        }
-
-        if (obj instanceof DERInteger)
-        {
-            DERInteger predefinedBiometricTypeObj = DERInteger.getInstance(obj);
-            int  predefinedBiometricType = predefinedBiometricTypeObj.getValue().intValue();
-
-            return new TypeOfBiometricData(predefinedBiometricType);
-        }
-        else if (obj instanceof DERObjectIdentifier)
-        {
-            DERObjectIdentifier BiometricDataID = DERObjectIdentifier.getInstance(obj);
-            return new TypeOfBiometricData(BiometricDataID);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance");
-    }
-        
-    public TypeOfBiometricData(int predefinedBiometricType)
-    {
-        if (predefinedBiometricType == PICTURE || predefinedBiometricType == HANDWRITTEN_SIGNATURE)
-        {
-                obj = new DERInteger(predefinedBiometricType);
-        }
-        else
-        {
-            throw new IllegalArgumentException("unknow PredefinedBiometricType : " + predefinedBiometricType);
-        }        
-    }
-    
-    public TypeOfBiometricData(DERObjectIdentifier BiometricDataID)
-    {
-        obj = BiometricDataID;
-    }
-    
-    public boolean isPredefined()
-    {
-        return obj instanceof DERInteger;
-    }
-    
-    public int getPredefinedBiometricType()
-    {
-        return ((DERInteger)obj).getValue().intValue();
-    }
-    
-    public DERObjectIdentifier getBiometricDataOid()
-    {
-        return (DERObjectIdentifier)obj;
-    }
-    
-    public DERObject toASN1Object() 
-    {        
-        return obj.getDERObject();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
deleted file mode 100644
index 73fc962..0000000
--- a/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.bouncycastle.asn1.x509.sigi;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-
-/**
- * Object Identifiers of SigI specifciation (German Signature Law
- * Interoperability specification).
- */
-public final class SigIObjectIdentifiers
-{
-    private SigIObjectIdentifiers()
-    {
-    }
-
-    public final static DERObjectIdentifier id_sigi = new DERObjectIdentifier("1.3.36.8");
-
-    /**
-     * Key purpose IDs for German SigI (Signature Interoperability
-     * Specification)
-     */
-    public final static DERObjectIdentifier id_sigi_kp = new DERObjectIdentifier(id_sigi + ".2");
-
-    /**
-     * Certificate policy IDs for German SigI (Signature Interoperability
-     * Specification)
-     */
-    public final static DERObjectIdentifier id_sigi_cp = new DERObjectIdentifier(id_sigi + ".1");
-
-    /**
-     * Other Name IDs for German SigI (Signature Interoperability Specification)
-     */
-    public final static DERObjectIdentifier id_sigi_on = new DERObjectIdentifier(id_sigi + ".4");
-
-    /**
-     * To be used for for the generation of directory service certificates.
-     */
-    public static final DERObjectIdentifier id_sigi_kp_directoryService = new DERObjectIdentifier(id_sigi_kp + ".1");
-
-    /**
-     * ID for PersonalData
-     */
-    public static final DERObjectIdentifier id_sigi_on_personalData = new DERObjectIdentifier(id_sigi_on + ".1");
-
-    /**
-     * Certificate is conform to german signature law.
-     */
-    public static final DERObjectIdentifier id_sigi_cp_sigconform = new DERObjectIdentifier(id_sigi_cp + ".1");
-
-}
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
index c313572..5473d48 100644
--- a/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
+++ b/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
@@ -79,7 +79,7 @@
     static final DERObjectIdentifier    c2onb239v4 = new DERObjectIdentifier(cTwoCurve + ".14");
     static final DERObjectIdentifier    c2onb239v5 = new DERObjectIdentifier(cTwoCurve + ".15");
     static final DERObjectIdentifier    c2pnb272w1 = new DERObjectIdentifier(cTwoCurve + ".16");
-    static final DERObjectIdentifier    c2png304v1 = new DERObjectIdentifier(cTwoCurve + ".17");
+    static final DERObjectIdentifier    c2pnb304w1 = new DERObjectIdentifier(cTwoCurve + ".17");
     static final DERObjectIdentifier    c2tnb359v1 = new DERObjectIdentifier(cTwoCurve + ".18");
     static final DERObjectIdentifier    c2pnb368w1 = new DERObjectIdentifier(cTwoCurve + ".19");
     static final DERObjectIdentifier    c2tnb431r1 = new DERObjectIdentifier(cTwoCurve + ".20");
@@ -117,5 +117,26 @@
      *         us(840) x9-57 (10040) x9cm(4) 3 }
      */
     public static final DERObjectIdentifier id_dsa_with_sha1 = new DERObjectIdentifier("1.2.840.10040.4.3");
+
+    /**
+     * X9.63
+     */
+    public static final DERObjectIdentifier x9_63_scheme = new DERObjectIdentifier("1.3.133.16.840.63.0");
+    public static final DERObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = new DERObjectIdentifier(x9_63_scheme + ".2");
+    public static final DERObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = new DERObjectIdentifier(x9_63_scheme + ".3");
+    public static final DERObjectIdentifier mqvSinglePass_sha1kdf_scheme = new DERObjectIdentifier(x9_63_scheme + ".16");
+
+    /**
+     * X9.42
+     */
+    public static final DERObjectIdentifier x9_42_schemes = new DERObjectIdentifier("1.2.840.10046.3");
+    public static final DERObjectIdentifier dhStatic = new DERObjectIdentifier(x9_42_schemes + ".1");
+    public static final DERObjectIdentifier dhEphem = new DERObjectIdentifier(x9_42_schemes + ".2");
+    public static final DERObjectIdentifier dhOneFlow = new DERObjectIdentifier(x9_42_schemes + ".3");
+    public static final DERObjectIdentifier dhHybrid1 = new DERObjectIdentifier(x9_42_schemes + ".4");
+    public static final DERObjectIdentifier dhHybrid2 = new DERObjectIdentifier(x9_42_schemes + ".5");
+    public static final DERObjectIdentifier dhHybridOneFlow = new DERObjectIdentifier(x9_42_schemes + ".6");
+    public static final DERObjectIdentifier mqv2 = new DERObjectIdentifier(x9_42_schemes + ".7");
+    public static final DERObjectIdentifier mqv1 = new DERObjectIdentifier(x9_42_schemes + ".8");
 }
 
diff --git a/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java b/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java
deleted file mode 100644
index 205132e..0000000
--- a/src/main/java/org/bouncycastle/crypto/BufferedAsymmetricBlockCipher.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package org.bouncycastle.crypto;
-
-/**
- * a buffer wrapper for an asymmetric block cipher, allowing input
- * to be accumulated in a piecemeal fashion until final processing.
- */
-public class BufferedAsymmetricBlockCipher
-{
-    protected byte[]        buf;
-    protected int           bufOff;
-
-    private final AsymmetricBlockCipher   cipher;
-
-    /**
-     * base constructor.
-     *
-     * @param cipher the cipher this buffering object wraps.
-     */
-    public BufferedAsymmetricBlockCipher(
-        AsymmetricBlockCipher     cipher)
-    {
-        this.cipher = cipher;
-    }
-
-    /**
-     * return the underlying cipher for the buffer.
-     *
-     * @return the underlying cipher for the buffer.
-     */
-    public AsymmetricBlockCipher getUnderlyingCipher()
-    {
-        return cipher;
-    }
-
-    /**
-     * return the amount of data sitting in the buffer.
-     *
-     * @return the amount of data sitting in the buffer.
-     */
-    public int getBufferPosition()
-    {
-        return bufOff;
-    }
-
-    /**
-     * initialise the buffer and the underlying cipher.
-     *
-     * @param forEncryption if true the cipher is initialised for
-     *  encryption, if false for decryption.
-     * @param params the key and other data required by the cipher.
-     */
-    public void init(
-        boolean             forEncryption,
-        CipherParameters    params)
-    {
-        reset();
-
-        cipher.init(forEncryption, params);
-
-        buf = new byte[cipher.getInputBlockSize()];
-        bufOff = 0;
-    }
-
-    /**
-     * returns the largest size an input block can be.
-     *
-     * @return maximum size for an input block.
-     */
-    public int getInputBlockSize()
-    {
-        return cipher.getInputBlockSize();
-    }
-
-    /**
-     * returns the maximum size of the block produced by this cipher.
-     *
-     * @return maximum size of the output block produced by the cipher.
-     */
-    public int getOutputBlockSize()
-    {
-        return cipher.getOutputBlockSize();
-    }
-
-    /**
-     * add another byte for processing.
-     * 
-     * @param in the input byte.
-     */
-    public void processByte(
-        byte        in)
-    {
-        if (bufOff > buf.length)
-        {
-            throw new DataLengthException("attempt to process message to long for cipher");
-        }
-
-        buf[bufOff++] = in;
-    }
-
-    /**
-     * add len bytes to the buffer for processing.
-     *
-     * @param in the input data
-     * @param inOff offset into the in array where the data starts
-     * @param len the length of the block to be processed.
-     */
-    public void processBytes(
-        byte[]      in,
-        int         inOff,
-        int         len)
-    {
-        if (len == 0)
-        {
-            return;
-        }
-
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        if (bufOff + len > buf.length)
-        {
-            throw new DataLengthException("attempt to process message to long for cipher");
-        }
-
-        System.arraycopy(in, inOff, buf, bufOff, len);
-        bufOff += len;
-    }
-
-    /**
-     * process the contents of the buffer using the underlying
-     * cipher.
-     *
-     * @return the result of the encryption/decryption process on the
-     * buffer.
-     * @exception InvalidCipherTextException if we are given a garbage block.
-     */
-    public byte[] doFinal()
-        throws InvalidCipherTextException
-    {
-        byte[] out = cipher.processBlock(buf, 0, bufOff);
-
-        reset();
-
-        return out;
-    }
-
-    /**
-     * Reset the buffer and the underlying cipher.
-     */
-    public void reset()
-    {
-        /*
-         * clean the buffer.
-         */
-        if (buf != null)
-        {
-            for (int i = 0; i < buf.length; i++)
-            {
-                buf[0] = 0;
-            }
-        }
-
-        bufOff = 0;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
index 53d776a..7f8adec 100644
--- a/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -127,32 +127,17 @@
 
     /**
      * return the size of the output buffer required for an update plus a
-     * doFinal with an input of len bytes.
+     * doFinal with an input of 'length' bytes.
      *
-     * @param len the length of the input.
+     * @param length the length of the input.
      * @return the space required to accommodate a call to update and doFinal
-     * with len bytes of input.
+     * with 'length' bytes of input.
      */
     public int getOutputSize(
-        int len)
+        int length)
     {
-        int total       = len + bufOff;
-        int leftOver;
-
-        if (pgpCFB)
-        {
-            leftOver    = total % buf.length - (cipher.getBlockSize() + 2);
-        }
-        else
-        {
-            leftOver    = total % buf.length;
-            if (leftOver == 0)
-            {
-                return total;
-            }
-        }
-
-        return total - leftOver + buf.length;
+        // Note: Can assume partialBlockOkay is true for purposes of this calculation
+        return length + bufOff;
     }
 
     /**
diff --git a/src/main/java/org/bouncycastle/crypto/Mac.java b/src/main/java/org/bouncycastle/crypto/Mac.java
index 8c122c8..c00cd58 100644
--- a/src/main/java/org/bouncycastle/crypto/Mac.java
+++ b/src/main/java/org/bouncycastle/crypto/Mac.java
@@ -50,7 +50,7 @@
         throws DataLengthException, IllegalStateException;
 
     /**
-     * Compute the final statge of the MAC writing the output to the out
+     * Compute the final stage of the MAC writing the output to the out
      * parameter.
      * <p>
      * doFinal leaves the MAC in the same state it was after the last init.
diff --git a/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java b/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
index aaa6015..ae4ffdc 100644
--- a/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto;
 
+import org.bouncycastle.util.Strings;
+
 /**
  * super class for all Password Based Encryption (PBE) parameter generator classes.
  */
@@ -113,11 +115,24 @@
 
     /**
      * converts a password to a byte array according to the scheme in
-     * PKCS12 (unicode, big endian, 2 zero pad bytes at the end).
+     * PKCS5 (UTF-8, no padding)
      *
      * @param password a character array reqpresenting the password.
      * @return a byte array representing the password.
      */
+    public static byte[] PKCS5PasswordToUTF8Bytes(
+        char[]  password)
+    {
+        return Strings.toUTF8ByteArray(password);
+    }
+
+    /**
+     * converts a password to a byte array according to the scheme in
+     * PKCS12 (unicode, big endian, 2 zero pad bytes at the end).
+     *
+     * @param password a character array representing the password.
+     * @return a byte array representing the password.
+     */
     public static byte[] PKCS12PasswordToBytes(
         char[]  password)
     {
diff --git a/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
deleted file mode 100644
index 8fdd232..0000000
--- a/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.bouncycastle.crypto;
-
-/**
- * a wrapper for block ciphers with a single byte block size, so that they
- * can be treated like stream ciphers.
- */
-public class StreamBlockCipher
-    implements StreamCipher
-{
-    private BlockCipher  cipher;
-
-    private byte[]  oneByte = new byte[1];
-
-    /**
-     * basic constructor.
-     *
-     * @param cipher the block cipher to be wrapped.
-     * @exception IllegalArgumentException if the cipher has a block size other than
-     * one.
-     */
-    public StreamBlockCipher(
-        BlockCipher cipher)
-    {
-        if (cipher.getBlockSize() != 1)
-        {
-            throw new IllegalArgumentException("block cipher block size != 1.");
-        }
-
-        this.cipher = cipher;
-    }
-
-    /**
-     * initialise the underlying cipher.
-     *
-     * @param forEncryption true if we are setting up for encryption, false otherwise.
-     * @param params the necessary parameters for the underlying cipher to be initialised.
-     */
-    public void init(
-        boolean forEncryption,
-        CipherParameters params)
-    {
-        cipher.init(forEncryption, params);
-    }
-
-    /**
-     * return the name of the algorithm we are wrapping.
-     *
-     * @return the name of the algorithm we are wrapping.
-     */
-    public String getAlgorithmName()
-    {
-        return cipher.getAlgorithmName();
-    }
-
-    /**
-     * encrypt/decrypt a single byte returning the result.
-     *
-     * @param in the byte to be processed.
-     * @return the result of processing the input byte.
-     */
-    public byte returnByte(
-        byte    in)
-    {
-        oneByte[0] = in;
-
-        cipher.processBlock(oneByte, 0, oneByte, 0);
-
-        return oneByte[0];
-    }
-
-    /**
-     * process a block of bytes from in putting the result into out.
-     * 
-     * @param in the input byte array.
-     * @param inOff the offset into the in array where the data to be processed starts.
-     * @param len the number of bytes to be processed.
-     * @param out the output buffer the processed bytes go into.   
-     * @param outOff the offset into the output byte array the processed data stars at.
-     * @exception DataLengthException if the output buffer is too small.
-     */
-    public void processBytes(
-        byte[]  in,
-        int     inOff,
-        int     len,
-        byte[]  out,
-        int     outOff)
-        throws DataLengthException
-    {
-        if (outOff + len > out.length)
-        {
-            throw new DataLengthException("output buffer too small in processBytes()");
-        }
-
-        for (int i = 0; i != len; i++)
-        {
-                cipher.processBlock(in, inOff + i, out, outOff + i);
-        }
-    }
-
-    /**
-     * reset the underlying cipher. This leaves it in the same state
-     * it was at after the last init (if there was one).
-     */
-    public void reset()
-    {
-        cipher.reset();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/StreamCipher.java b/src/main/java/org/bouncycastle/crypto/StreamCipher.java
deleted file mode 100644
index afa6296..0000000
--- a/src/main/java/org/bouncycastle/crypto/StreamCipher.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.bouncycastle.crypto;
-
-/**
- * the interface stream ciphers conform to.
- */
-public interface StreamCipher
-{
-    /**
-     * Initialise the cipher.
-     *
-     * @param forEncryption if true the cipher is initialised for
-     *  encryption, if false for decryption.
-     * @param params the key and other data required by the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
-     */
-    public void init(boolean forEncryption, CipherParameters params)
-        throws IllegalArgumentException;
-
-    /**
-     * Return the name of the algorithm the cipher implements.
-     *
-     * @return the name of the algorithm the cipher implements.
-     */
-    public String getAlgorithmName();
-
-    /**
-     * encrypt/decrypt a single byte returning the result.
-     *
-     * @param in the byte to be processed.
-     * @return the result of processing the input byte.
-     */
-    public byte returnByte(byte in);
-
-    /**
-     * process a block of bytes from in putting the result into out.
-     *
-     * @param in the input byte array.
-     * @param inOff the offset into the in array where the data to be processed starts.
-     * @param len the number of bytes to be processed.
-     * @param out the output buffer the processed bytes go into.
-     * @param outOff the offset into the output byte array the processed data stars at.
-     * @exception DataLengthException if the output buffer is too small.
-     */
-    public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
-        throws DataLengthException;
-
-    /**
-     * reset the cipher. This leaves it in the same state
-     * it was at after the last init (if there was one).
-     */
-    public void reset();
-}
diff --git a/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java b/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
deleted file mode 100644
index 2b27feb..0000000
--- a/src/main/java/org/bouncycastle/crypto/agreement/DHAgreement.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.bouncycastle.crypto.agreement;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.params.DHParameters;
-import org.bouncycastle.crypto.params.DHPublicKeyParameters;
-import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-
-/**
- * a Diffie-Hellman key exchange engine.
- * <p>
- * note: This uses MTI/A0 key agreement in order to make the key agreement
- * secure against passive attacks. If you're doing Diffie-Hellman and both
- * parties have long term public keys you should look at using this. For
- * further information have a look at RFC 2631.
- * <p>
- * It's possible to extend this to more than two parties as well, for the moment
- * that is left as an exercise for the reader.
- */
-public class DHAgreement
-{
-    private DHPrivateKeyParameters  key;
-    private DHParameters            dhParams;
-    private BigInteger              privateValue;
-    private SecureRandom            random;
-
-    public void init(
-        CipherParameters    param)
-    {
-        AsymmetricKeyParameter  kParam;
-
-        if (param instanceof ParametersWithRandom)
-        {
-            ParametersWithRandom    rParam = (ParametersWithRandom)param;
-
-            this.random = rParam.getRandom();
-            kParam = (AsymmetricKeyParameter)rParam.getParameters();
-        }
-        else
-        {
-            this.random = new SecureRandom();
-            kParam = (AsymmetricKeyParameter)param;
-        }
-
-        
-        if (!(kParam instanceof DHPrivateKeyParameters))
-        {
-            throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters");
-        }
-
-        this.key = (DHPrivateKeyParameters)kParam;
-        this.dhParams = key.getParameters();
-    }
-
-    /**
-     * calculate our initial message.
-     */
-    public BigInteger calculateMessage()
-    {
-        this.privateValue = new BigInteger(
-                                    dhParams.getP().bitLength() - 1, 0, random);
-
-        return dhParams.getG().modPow(privateValue, dhParams.getP());
-    }
-
-    /**
-     * given a message from a given party and the coresponding public key
-     * calculate the next message in the agreement sequence. In this case
-     * this will represent the shared secret.
-     */
-    public BigInteger calculateAgreement(
-        DHPublicKeyParameters   pub,
-        BigInteger              message)
-    {
-        if (!pub.getParameters().equals(dhParams))
-        {
-            throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters.");
-        }
-
-        return message.modPow(key.getX(), dhParams.getP()).multiply(pub.getY().modPow(privateValue, dhParams.getP())).mod(dhParams.getP());
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
index 1a39de3..40893bf 100644
--- a/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
+++ b/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.crypto.agreement;
 
 import java.math.BigInteger;
-import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.CipherParameters;
@@ -23,7 +22,6 @@
 {
     private DHPrivateKeyParameters  key;
     private DHParameters            dhParams;
-    private SecureRandom            random;
 
     public void init(
         CipherParameters    param)
@@ -32,18 +30,14 @@
 
         if (param instanceof ParametersWithRandom)
         {
-            ParametersWithRandom    rParam = (ParametersWithRandom)param;
-
-            this.random = rParam.getRandom();
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
             kParam = (AsymmetricKeyParameter)rParam.getParameters();
         }
         else
         {
-            this.random = new SecureRandom();
             kParam = (AsymmetricKeyParameter)param;
         }
 
-        
         if (!(kParam instanceof DHPrivateKeyParameters))
         {
             throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters");
diff --git a/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
index 23af605..22d457b 100644
--- a/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
+++ b/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.crypto.digests;
 
 import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.util.Pack;
 
 /**
  * Base class for SHA-384 and SHA-512.
@@ -162,36 +163,14 @@
         byte[]  in,
         int     inOff)
     {
-        W[wOff++] = ((long)(in[inOff] & 0xff) << 56)
-                    | ((long)(in[inOff + 1] & 0xff) << 48)
-                    | ((long)(in[inOff + 2] & 0xff) << 40)
-                    | ((long)(in[inOff + 3] & 0xff) << 32)
-                    | ((long)(in[inOff + 4] & 0xff) << 24)
-                    | ((long)(in[inOff + 5] & 0xff) << 16)
-                    | ((long)(in[inOff + 6] & 0xff) << 8)
-                    | ((in[inOff + 7] & 0xff)); 
+        W[wOff] = Pack.bigEndianToLong(in, inOff);
 
-        if (wOff == 16)
+        if (++wOff == 16)
         {
             processBlock();
         }
     }
 
-    protected void unpackWord(
-        long    word,
-        byte[]  out,
-        int     outOff)
-    {
-        out[outOff]     = (byte)(word >>> 56);
-        out[outOff + 1] = (byte)(word >>> 48);
-        out[outOff + 2] = (byte)(word >>> 40);
-        out[outOff + 3] = (byte)(word >>> 32);
-        out[outOff + 4] = (byte)(word >>> 24);
-        out[outOff + 5] = (byte)(word >>> 16);
-        out[outOff + 6] = (byte)(word >>> 8);
-        out[outOff + 7] = (byte)word;
-    }
-
     /**
      * adjust the byte counts so that byteCount2 represents the
      * upper long (less 3 bits) word of the byte count.
diff --git a/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java b/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
deleted file mode 100644
index 2a8084f..0000000
--- a/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package org.bouncycastle.crypto.digests;
-
-
-/**
- * implementation of MD4 as RFC 1320 by R. Rivest, MIT Laboratory for
- * Computer Science and RSA Data Security, Inc.
- * <p>
- * <b>NOTE</b>: This algorithm is only included for backwards compatability
- * with legacy applications, it's not secure, don't use it for anything new!
- */
-public class MD4Digest
-    extends GeneralDigest
-{
-    private static final int    DIGEST_LENGTH = 16;
-
-    private int     H1, H2, H3, H4;         // IV's
-
-    private int[]   X = new int[16];
-    private int     xOff;
-
-    /**
-     * Standard constructor
-     */
-    public MD4Digest()
-    {
-        reset();
-    }
-
-    /**
-     * Copy constructor.  This will copy the state of the provided
-     * message digest.
-     */
-    public MD4Digest(MD4Digest t)
-    {
-        super(t);
-
-        H1 = t.H1;
-        H2 = t.H2;
-        H3 = t.H3;
-        H4 = t.H4;
-
-        System.arraycopy(t.X, 0, X, 0, t.X.length);
-        xOff = t.xOff;
-    }
-
-    public String getAlgorithmName()
-    {
-        return "MD4";
-    }
-
-    public int getDigestSize()
-    {
-        return DIGEST_LENGTH;
-    }
-
-    protected void processWord(
-        byte[]  in,
-        int     inOff)
-    {
-        X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
-            | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); 
-
-        if (xOff == 16)
-        {
-            processBlock();
-        }
-    }
-
-    protected void processLength(
-        long    bitLength)
-    {
-        if (xOff > 14)
-        {
-            processBlock();
-        }
-
-        X[14] = (int)(bitLength & 0xffffffff);
-        X[15] = (int)(bitLength >>> 32);
-    }
-
-    private void unpackWord(
-        int     word,
-        byte[]  out,
-        int     outOff)
-    {
-        out[outOff]     = (byte)word;
-        out[outOff + 1] = (byte)(word >>> 8);
-        out[outOff + 2] = (byte)(word >>> 16);
-        out[outOff + 3] = (byte)(word >>> 24);
-    }
-
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
-    {
-        finish();
-
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 4);
-        unpackWord(H3, out, outOff + 8);
-        unpackWord(H4, out, outOff + 12);
-
-        reset();
-
-        return DIGEST_LENGTH;
-    }
-
-    /**
-     * reset the chaining variables to the IV values.
-     */
-    public void reset()
-    {
-        super.reset();
-
-        H1 = 0x67452301;
-        H2 = 0xefcdab89;
-        H3 = 0x98badcfe;
-        H4 = 0x10325476;
-
-        xOff = 0;
-
-        for (int i = 0; i != X.length; i++)
-        {
-            X[i] = 0;
-        }
-    }
-
-    //
-    // round 1 left rotates
-    //
-    private static final int S11 = 3;
-    private static final int S12 = 7;
-    private static final int S13 = 11;
-    private static final int S14 = 19;
-
-    //
-    // round 2 left rotates
-    //
-    private static final int S21 = 3;
-    private static final int S22 = 5;
-    private static final int S23 = 9;
-    private static final int S24 = 13;
-
-    //
-    // round 3 left rotates
-    //
-    private static final int S31 = 3;
-    private static final int S32 = 9;
-    private static final int S33 = 11;
-    private static final int S34 = 15;
-
-    /*
-     * rotate int x left n bits.
-     */
-    private int rotateLeft(
-        int x,
-        int n)
-    {
-        return (x << n) | (x >>> (32 - n));
-    }
-
-    /*
-     * F, G, H and I are the basic MD4 functions.
-     */
-    private int F(
-        int u,
-        int v,
-        int w)
-    {
-        return (u & v) | (~u & w);
-    }
-
-    private int G(
-        int u,
-        int v,
-        int w)
-    {
-        return (u & v) | (u & w) | (v & w);
-    }
-
-    private int H(
-        int u,
-        int v,
-        int w)
-    {
-        return u ^ v ^ w;
-    }
-
-    protected void processBlock()
-    {
-        int a = H1;
-        int b = H2;
-        int c = H3;
-        int d = H4;
-
-        //
-        // Round 1 - F cycle, 16 times.
-        //
-        a = rotateLeft(a + F(b, c, d) + X[ 0], S11);
-        d = rotateLeft(d + F(a, b, c) + X[ 1], S12);
-        c = rotateLeft(c + F(d, a, b) + X[ 2], S13);
-        b = rotateLeft(b + F(c, d, a) + X[ 3], S14);
-        a = rotateLeft(a + F(b, c, d) + X[ 4], S11);
-        d = rotateLeft(d + F(a, b, c) + X[ 5], S12);
-        c = rotateLeft(c + F(d, a, b) + X[ 6], S13);
-        b = rotateLeft(b + F(c, d, a) + X[ 7], S14);
-        a = rotateLeft(a + F(b, c, d) + X[ 8], S11);
-        d = rotateLeft(d + F(a, b, c) + X[ 9], S12);
-        c = rotateLeft(c + F(d, a, b) + X[10], S13);
-        b = rotateLeft(b + F(c, d, a) + X[11], S14);
-        a = rotateLeft(a + F(b, c, d) + X[12], S11);
-        d = rotateLeft(d + F(a, b, c) + X[13], S12);
-        c = rotateLeft(c + F(d, a, b) + X[14], S13);
-        b = rotateLeft(b + F(c, d, a) + X[15], S14);
-
-        //
-        // Round 2 - G cycle, 16 times.
-        //
-        a = rotateLeft(a + G(b, c, d) + X[ 0] + 0x5a827999, S21);
-        d = rotateLeft(d + G(a, b, c) + X[ 4] + 0x5a827999, S22);
-        c = rotateLeft(c + G(d, a, b) + X[ 8] + 0x5a827999, S23);
-        b = rotateLeft(b + G(c, d, a) + X[12] + 0x5a827999, S24);
-        a = rotateLeft(a + G(b, c, d) + X[ 1] + 0x5a827999, S21);
-        d = rotateLeft(d + G(a, b, c) + X[ 5] + 0x5a827999, S22);
-        c = rotateLeft(c + G(d, a, b) + X[ 9] + 0x5a827999, S23);
-        b = rotateLeft(b + G(c, d, a) + X[13] + 0x5a827999, S24);
-        a = rotateLeft(a + G(b, c, d) + X[ 2] + 0x5a827999, S21);
-        d = rotateLeft(d + G(a, b, c) + X[ 6] + 0x5a827999, S22);
-        c = rotateLeft(c + G(d, a, b) + X[10] + 0x5a827999, S23);
-        b = rotateLeft(b + G(c, d, a) + X[14] + 0x5a827999, S24);
-        a = rotateLeft(a + G(b, c, d) + X[ 3] + 0x5a827999, S21);
-        d = rotateLeft(d + G(a, b, c) + X[ 7] + 0x5a827999, S22);
-        c = rotateLeft(c + G(d, a, b) + X[11] + 0x5a827999, S23);
-        b = rotateLeft(b + G(c, d, a) + X[15] + 0x5a827999, S24);
-
-        //
-        // Round 3 - H cycle, 16 times.
-        //
-        a = rotateLeft(a + H(b, c, d) + X[ 0] + 0x6ed9eba1, S31);
-        d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x6ed9eba1, S32);
-        c = rotateLeft(c + H(d, a, b) + X[ 4] + 0x6ed9eba1, S33);
-        b = rotateLeft(b + H(c, d, a) + X[12] + 0x6ed9eba1, S34);
-        a = rotateLeft(a + H(b, c, d) + X[ 2] + 0x6ed9eba1, S31);
-        d = rotateLeft(d + H(a, b, c) + X[10] + 0x6ed9eba1, S32);
-        c = rotateLeft(c + H(d, a, b) + X[ 6] + 0x6ed9eba1, S33);
-        b = rotateLeft(b + H(c, d, a) + X[14] + 0x6ed9eba1, S34);
-        a = rotateLeft(a + H(b, c, d) + X[ 1] + 0x6ed9eba1, S31);
-        d = rotateLeft(d + H(a, b, c) + X[ 9] + 0x6ed9eba1, S32);
-        c = rotateLeft(c + H(d, a, b) + X[ 5] + 0x6ed9eba1, S33);
-        b = rotateLeft(b + H(c, d, a) + X[13] + 0x6ed9eba1, S34);
-        a = rotateLeft(a + H(b, c, d) + X[ 3] + 0x6ed9eba1, S31);
-        d = rotateLeft(d + H(a, b, c) + X[11] + 0x6ed9eba1, S32);
-        c = rotateLeft(c + H(d, a, b) + X[ 7] + 0x6ed9eba1, S33);
-        b = rotateLeft(b + H(c, d, a) + X[15] + 0x6ed9eba1, S34);
-
-        H1 += a;
-        H2 += b;
-        H3 += c;
-        H4 += d;
-
-        //
-        // reset the offset and clean out the word buffer.
-        //
-        xOff = 0;
-        for (int i = 0; i != X.length; i++)
-        {
-            X[i] = 0;
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java b/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java
new file mode 100644
index 0000000..f53c7cd
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bouncycastle.crypto.digests;
+
+import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
+import org.bouncycastle.crypto.ExtendedDigest;
+
+/**
+ * Implements the BouncyCastle Digest interface using OpenSSL's EVP API.
+ */
+public class OpenSSLDigest implements ExtendedDigest {
+
+    /**
+     * Holds the name of the hashing algorithm, e.g. "SHA-1";
+     */
+    private String algorithm;
+
+    /**
+     * Holds a pointer to the native message digest context.
+     */
+    private int ctx;
+
+    /**
+     * Holds a dummy buffer for writing single bytes to the digest.
+     */
+    private byte[] singleByte = new byte[1];
+
+    /**
+     * Creates a new OpenSSLMessageDigest instance for the given algorithm
+     * name.
+     *
+     * @param algorithm The name of the algorithm, e.g. "SHA1".
+     */
+    private OpenSSLDigest(String algorithm) {
+        this.algorithm = algorithm;
+
+        // We don't support MD2.
+        if ("MD2".equalsIgnoreCase(algorithm)) {
+            throw new RuntimeException(algorithm + " not supported");
+        }
+
+        ctx = NativeCrypto.EVP_new();
+        try {
+            NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
+        } catch (Exception ex) {
+            throw new RuntimeException(ex.getMessage() + " (" + algorithm + ")");
+        }
+    }
+
+    public int doFinal(byte[] out, int outOff) {
+        int i = NativeCrypto.EVP_DigestFinal(ctx, out, outOff);
+        reset();
+        return i;
+    }
+
+    public String getAlgorithmName() {
+        return algorithm;
+    }
+
+    public int getDigestSize() {
+        return NativeCrypto.EVP_DigestSize(ctx);
+    }
+
+    public int getByteLength() {
+        return NativeCrypto.EVP_DigestBlockSize(ctx);
+    }
+
+    public void reset() {
+        NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
+    }
+
+    public void update(byte in) {
+        singleByte[0] = in;
+        NativeCrypto.EVP_DigestUpdate(ctx, singleByte, 0, 1);
+    }
+
+    public void update(byte[] in, int inOff, int len) {
+        NativeCrypto.EVP_DigestUpdate(ctx, in, inOff, len);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        NativeCrypto.EVP_free(ctx);
+    }
+
+    public static class MD5 extends OpenSSLDigest {
+        public MD5() { super("MD5"); }
+    }
+
+    public static class SHA1 extends OpenSSLDigest {
+        public SHA1() { super("SHA-1"); }
+    }
+
+    public static class SHA256 extends OpenSSLDigest {
+        public SHA256() { super("SHA-256"); }
+    }
+
+    public static class SHA384 extends OpenSSLDigest {
+        public SHA384() { super("SHA-384"); }
+    }
+
+    public static class SHA512 extends OpenSSLDigest {
+        public SHA512() { super("SHA-512"); }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
index 9b282e9..7f8d30a 100644
--- a/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.util.Pack;
+
 /**
  * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
  *
@@ -56,26 +58,20 @@
         byte[]  in,
         int     inOff)
     {
-        X[xOff++] = (in[inOff] & 0xff) << 24 | (in[inOff + 1] & 0xff) << 16
-                    | (in[inOff + 2] & 0xff) << 8 | in[inOff + 3] & 0xff; 
+        // Note: Inlined for performance
+//        X[xOff] = Pack.bigEndianToInt(in, inOff);
+        int n = in[  inOff] << 24;
+        n |= (in[++inOff] & 0xff) << 16;
+        n |= (in[++inOff] & 0xff) << 8;
+        n |= (in[++inOff] & 0xff);
+        X[xOff] = n;
 
-        if (xOff == 16)
+        if (++xOff == 16)
         {
             processBlock();
         }        
     }
 
-    private void unpackWord(
-        int     word,
-        byte[]  out,
-        int     outOff)
-    {
-        out[outOff++] = (byte)(word >>> 24);
-        out[outOff++] = (byte)(word >>> 16);
-        out[outOff++] = (byte)(word >>> 8);
-        out[outOff++] = (byte)word;
-    }
-
     protected void processLength(
         long    bitLength)
     {
@@ -94,11 +90,11 @@
     {
         finish();
 
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 4);
-        unpackWord(H3, out, outOff + 8);
-        unpackWord(H4, out, outOff + 12);
-        unpackWord(H5, out, outOff + 16);
+        Pack.intToBigEndian(H1, out, outOff);
+        Pack.intToBigEndian(H2, out, outOff + 4);
+        Pack.intToBigEndian(H3, out, outOff + 8);
+        Pack.intToBigEndian(H4, out, outOff + 12);
+        Pack.intToBigEndian(H5, out, outOff + 16);
 
         reset();
 
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
deleted file mode 100644
index 392d12b..0000000
--- a/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package org.bouncycastle.crypto.digests;
-
-
-import org.bouncycastle.crypto.digests.GeneralDigest;
-
-
-/**
- * SHA-224 as described in RFC 3874
- * <pre>
- *         block  word  digest
- * SHA-1   512    32    160
- * SHA-224 512    32    224
- * SHA-256 512    32    256
- * SHA-384 1024   64    384
- * SHA-512 1024   64    512
- * </pre>
- */
-public class SHA224Digest
-    extends GeneralDigest
-{
-    private static final int    DIGEST_LENGTH = 28;
-
-    private int     H1, H2, H3, H4, H5, H6, H7, H8;
-
-    private int[]   X = new int[64];
-    private int     xOff;
-
-    /**
-     * Standard constructor
-     */
-    public SHA224Digest()
-    {
-        reset();
-    }
-
-    /**
-     * Copy constructor.  This will copy the state of the provided
-     * message digest.
-     */
-    public SHA224Digest(SHA224Digest t)
-    {
-        super(t);
-
-        H1 = t.H1;
-        H2 = t.H2;
-        H3 = t.H3;
-        H4 = t.H4;
-        H5 = t.H5;
-        H6 = t.H6;
-        H7 = t.H7;
-        H8 = t.H8;
-
-        System.arraycopy(t.X, 0, X, 0, t.X.length);
-        xOff = t.xOff;
-    }
-
-    public String getAlgorithmName()
-    {
-        return "SHA-224";
-    }
-
-    public int getDigestSize()
-    {
-        return DIGEST_LENGTH;
-    }
-
-    protected void processWord(
-        byte[]  in,
-        int     inOff)
-    {
-        X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16)
-                    | ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff)); 
-
-        if (xOff == 16)
-        {
-            processBlock();
-        }
-    }
-
-    private void unpackWord(
-        int     word,
-        byte[]  out,
-        int     outOff)
-    {
-        out[outOff]     = (byte)(word >>> 24);
-        out[outOff + 1] = (byte)(word >>> 16);
-        out[outOff + 2] = (byte)(word >>> 8);
-        out[outOff + 3] = (byte)word;
-    }
-
-    protected void processLength(
-        long    bitLength)
-    {
-        if (xOff > 14)
-        {
-            processBlock();
-        }
-
-        X[14] = (int)(bitLength >>> 32);
-        X[15] = (int)(bitLength & 0xffffffff);
-    }
-
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
-    {
-        finish();
-
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 4);
-        unpackWord(H3, out, outOff + 8);
-        unpackWord(H4, out, outOff + 12);
-        unpackWord(H5, out, outOff + 16);
-        unpackWord(H6, out, outOff + 20);
-        unpackWord(H7, out, outOff + 24);
-
-        reset();
-
-        return DIGEST_LENGTH;
-    }
-
-    /**
-     * reset the chaining variables
-     */
-    public void reset()
-    {
-        super.reset();
-
-        /* SHA-224 initial hash value
-         */
-
-        H1 = 0xc1059ed8;
-        H2 = 0x367cd507;
-        H3 = 0x3070dd17;
-        H4 = 0xf70e5939;
-        H5 = 0xffc00b31;
-        H6 = 0x68581511;
-        H7 = 0x64f98fa7;
-        H8 = 0xbefa4fa4;
-
-        xOff = 0;
-        for (int i = 0; i != X.length; i++)
-        {
-            X[i] = 0;
-        }
-    }
-
-    protected void processBlock()
-    {
-        //
-        // expand 16 word block into 64 word blocks.
-        //
-        for (int t = 16; t <= 63; t++)
-        {
-            X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
-        }
-
-        //
-        // set up working variables.
-        //
-        int     a = H1;
-        int     b = H2;
-        int     c = H3;
-        int     d = H4;
-        int     e = H5;
-        int     f = H6;
-        int     g = H7;
-        int     h = H8;
-
-
-        int t = 0;     
-        for(int i = 0; i < 8; i ++)
-        {
-            // t = 8 * i
-            h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++];
-            d += h;
-            h += Sum0(a) + Maj(a, b, c);
-
-            // t = 8 * i + 1
-            g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++];
-            c += g;
-            g += Sum0(h) + Maj(h, a, b);
-
-            // t = 8 * i + 2
-            f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++];
-            b += f;
-            f += Sum0(g) + Maj(g, h, a);
-
-            // t = 8 * i + 3
-            e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++];
-            a += e;
-            e += Sum0(f) + Maj(f, g, h);
-
-            // t = 8 * i + 4
-            d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++];
-            h += d;
-            d += Sum0(e) + Maj(e, f, g);
-
-            // t = 8 * i + 5
-            c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++];
-            g += c;
-            c += Sum0(d) + Maj(d, e, f);
-
-            // t = 8 * i + 6
-            b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++];
-            f += b;
-            b += Sum0(c) + Maj(c, d, e);
-
-            // t = 8 * i + 7
-            a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++];
-            e += a;
-            a += Sum0(b) + Maj(b, c, d);
-        }
-
-        H1 += a;
-        H2 += b;
-        H3 += c;
-        H4 += d;
-        H5 += e;
-        H6 += f;
-        H7 += g;
-        H8 += h;
-
-        //
-        // reset the offset and clean out the word buffer.
-        //
-        xOff = 0;
-        for (int i = 0; i < 16; i++)
-        {
-            X[i] = 0;
-        }
-    }
-
-    /* SHA-224 functions */
-    private int Ch(
-        int    x,
-        int    y,
-        int    z)
-    {
-        return ((x & y) ^ ((~x) & z));
-    }
-
-    private int Maj(
-        int    x,
-        int    y,
-        int    z)
-    {
-        return ((x & y) ^ (x & z) ^ (y & z));
-    }
-
-    private int Sum0(
-        int    x)
-    {
-        return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
-    }
-
-    private int Sum1(
-        int    x)
-    {
-        return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
-    }
-
-    private int Theta0(
-        int    x)
-    {
-        return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
-    }
-
-    private int Theta1(
-        int    x)
-    {
-        return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
-    }
-
-    /* SHA-224 Constants
-     * (represent the first 32 bits of the fractional parts of the
-     * cube roots of the first sixty-four prime numbers)
-     */
-    static final int K[] = {
-        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
-        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
-        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
-        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
-        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
-    };
-}
-
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
index d9cbc44..abd9c1b 100644
--- a/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
+++ b/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -2,6 +2,7 @@
 
 
 import org.bouncycastle.crypto.digests.GeneralDigest;
+import org.bouncycastle.crypto.util.Pack;
 
 
 /**
@@ -68,26 +69,20 @@
         byte[]  in,
         int     inOff)
     {
-        X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16)
-                    | ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff)); 
+        // Note: Inlined for performance
+//        X[xOff] = Pack.bigEndianToInt(in, inOff);
+        int n = in[inOff] << 24;
+        n |= (in[++inOff] & 0xff) << 16;
+        n |= (in[++inOff] & 0xff) << 8;
+        n |= (in[++inOff] & 0xff);
+        X[xOff] = n;
 
-        if (xOff == 16)
+        if (++xOff == 16)
         {
             processBlock();
         }
     }
 
-    private void unpackWord(
-        int     word,
-        byte[]  out,
-        int     outOff)
-    {
-        out[outOff]     = (byte)(word >>> 24);
-        out[outOff + 1] = (byte)(word >>> 16);
-        out[outOff + 2] = (byte)(word >>> 8);
-        out[outOff + 3] = (byte)word;
-    }
-
     protected void processLength(
         long    bitLength)
     {
@@ -106,14 +101,14 @@
     {
         finish();
 
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 4);
-        unpackWord(H3, out, outOff + 8);
-        unpackWord(H4, out, outOff + 12);
-        unpackWord(H5, out, outOff + 16);
-        unpackWord(H6, out, outOff + 20);
-        unpackWord(H7, out, outOff + 24);
-        unpackWord(H8, out, outOff + 28);
+        Pack.intToBigEndian(H1, out, outOff);
+        Pack.intToBigEndian(H2, out, outOff + 4);
+        Pack.intToBigEndian(H3, out, outOff + 8);
+        Pack.intToBigEndian(H4, out, outOff + 12);
+        Pack.intToBigEndian(H5, out, outOff + 16);
+        Pack.intToBigEndian(H6, out, outOff + 20);
+        Pack.intToBigEndian(H7, out, outOff + 24);
+        Pack.intToBigEndian(H8, out, outOff + 28);
 
         reset();
 
@@ -174,44 +169,52 @@
         for(int i = 0; i < 8; i ++)
         {
             // t = 8 * i
-            h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++];
+            h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
             d += h;
             h += Sum0(a) + Maj(a, b, c);
+            ++t;
 
             // t = 8 * i + 1
-            g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++];
+            g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
             c += g;
             g += Sum0(h) + Maj(h, a, b);
+            ++t;
 
             // t = 8 * i + 2
-            f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++];
+            f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
             b += f;
             f += Sum0(g) + Maj(g, h, a);
+            ++t;
 
             // t = 8 * i + 3
-            e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++];
+            e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
             a += e;
             e += Sum0(f) + Maj(f, g, h);
+            ++t;
 
             // t = 8 * i + 4
-            d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++];
+            d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
             h += d;
             d += Sum0(e) + Maj(e, f, g);
+            ++t;
 
             // t = 8 * i + 5
-            c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++];
+            c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
             g += c;
             c += Sum0(d) + Maj(d, e, f);
+            ++t;
 
             // t = 8 * i + 6
-            b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++];
+            b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
             f += b;
             b += Sum0(c) + Maj(c, d, e);
+            ++t;
 
             // t = 8 * i + 7
-            a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++];
+            a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
             e += a;
             a += Sum0(b) + Maj(b, c, d);
+            ++t;
         }
 
         H1 += a;
@@ -284,7 +287,8 @@
         0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
         0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
         0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
         0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
     };
 }
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
index e2a9faa..cdd979a 100644
--- a/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
+++ b/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.util.Pack;
+
 
 /**
  * FIPS 180-2 implementation of SHA-384.
@@ -50,12 +52,12 @@
     {
         finish();
 
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 8);
-        unpackWord(H3, out, outOff + 16);
-        unpackWord(H4, out, outOff + 24);
-        unpackWord(H5, out, outOff + 32);
-        unpackWord(H6, out, outOff + 40);
+        Pack.longToBigEndian(H1, out, outOff);
+        Pack.longToBigEndian(H2, out, outOff + 8);
+        Pack.longToBigEndian(H3, out, outOff + 16);
+        Pack.longToBigEndian(H4, out, outOff + 24);
+        Pack.longToBigEndian(H5, out, outOff + 32);
+        Pack.longToBigEndian(H6, out, outOff + 40);
 
         reset();
 
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
index 1f4ae41..34a8e4e 100644
--- a/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
+++ b/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.util.Pack;
+
 
 /**
  * FIPS 180-2 implementation of SHA-512.
@@ -49,14 +51,14 @@
     {
         finish();
 
-        unpackWord(H1, out, outOff);
-        unpackWord(H2, out, outOff + 8);
-        unpackWord(H3, out, outOff + 16);
-        unpackWord(H4, out, outOff + 24);
-        unpackWord(H5, out, outOff + 32);
-        unpackWord(H6, out, outOff + 40);
-        unpackWord(H7, out, outOff + 48);
-        unpackWord(H8, out, outOff + 56);
+        Pack.longToBigEndian(H1, out, outOff);
+        Pack.longToBigEndian(H2, out, outOff + 8);
+        Pack.longToBigEndian(H3, out, outOff + 16);
+        Pack.longToBigEndian(H4, out, outOff + 24);
+        Pack.longToBigEndian(H5, out, outOff + 32);
+        Pack.longToBigEndian(H6, out, outOff + 40);
+        Pack.longToBigEndian(H7, out, outOff + 48);
+        Pack.longToBigEndian(H8, out, outOff + 56);
 
         reset();
 
diff --git a/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java b/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java
deleted file mode 100644
index 89033e8..0000000
--- a/src/main/java/org/bouncycastle/crypto/digests/ShortenedDigest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.bouncycastle.crypto.digests;
-
-import org.bouncycastle.crypto.ExtendedDigest;
-
-/**
- * Wrapper class that reduces the output length of a particular digest to
- * only the first n bytes of the digest function.
- */
-public class ShortenedDigest 
-    implements ExtendedDigest
-{
-    private ExtendedDigest baseDigest;
-    private int            length;
-    
-    /**
-     * Base constructor.
-     * 
-     * @param baseDigest underlying digest to use.
-     * @param length length in bytes of the output of doFinal.
-     * @exception IllegalArgumentException if baseDigest is null, or length is greater than baseDigest.getDigestSize().
-     */
-    public ShortenedDigest(
-        ExtendedDigest baseDigest,
-        int            length)
-    {
-        if (baseDigest == null)
-        {
-            throw new IllegalArgumentException("baseDigest must not be null");
-        }
-        
-        if (length > baseDigest.getDigestSize())
-        {
-            throw new IllegalArgumentException("baseDigest output not large enough to support length");
-        }
-        
-        this.baseDigest = baseDigest;
-        this.length = length;
-    }
-    
-    public String getAlgorithmName()
-    {
-        return baseDigest.getAlgorithmName() + "(" + length * 8 + ")";
-    }
-
-    public int getDigestSize()
-    {
-        return length;
-    }
-
-    public void update(byte in)
-    {
-        baseDigest.update(in);
-    }
-
-    public void update(byte[] in, int inOff, int len)
-    {
-        baseDigest.update(in, inOff, len);
-    }
-
-    public int doFinal(byte[] out, int outOff)
-    {
-        byte[] tmp = new byte[baseDigest.getDigestSize()];
-        
-        baseDigest.doFinal(tmp, 0);
-        
-        System.arraycopy(tmp, 0, out, outOff, length);
-        
-        return length;
-    }
-
-    public void reset()
-    {
-        baseDigest.reset();
-    }
-
-    public int getByteLength()
-    {
-        return baseDigest.getByteLength();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java b/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
index 7e30023..b4d84c6 100644
--- a/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
+++ b/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
@@ -54,7 +54,7 @@
             kParam = (RSAKeyParameters)param;
         }
 
-        engine.init(forEncryption, kParam);
+        engine.init(forEncryption, param);
 
         bitSize = kParam.getModulus().bitLength();
 
diff --git a/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index 11821a0..9e94d77 100644
--- a/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -1,15 +1,14 @@
 package org.bouncycastle.crypto.encodings;
 
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 
+import java.security.SecureRandom;
+
 /**
  * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
  */
@@ -18,6 +17,7 @@
 {
     private byte[]                  defHash;
     private Digest                  hash;
+    private Digest                  mgf1Hash;
 
     private AsymmetricBlockCipher   engine;
     private SecureRandom            random;
@@ -41,18 +41,28 @@
         Digest                      hash,
         byte[]                      encodingParams)
     {
+        this(cipher, hash, hash, encodingParams);
+    }
+
+    public OAEPEncoding(
+        AsymmetricBlockCipher       cipher,
+        Digest                      hash,
+        Digest                      mgf1Hash,
+        byte[]                      encodingParams)
+    {
         this.engine = cipher;
         this.hash = hash;
+        this.mgf1Hash = mgf1Hash;
         this.defHash = new byte[hash.getDigestSize()];
-        
+
         if (encodingParams != null)
         {
             hash.update(encodingParams, 0, encodingParams.length);
         }
-        
+
         hash.doFinal(defHash, 0);
     }
-    
+
     public AsymmetricBlockCipher getUnderlyingCipher()
     {
         return engine;
@@ -62,22 +72,18 @@
         boolean             forEncryption,
         CipherParameters    param)
     {
-        AsymmetricKeyParameter  kParam;
-
         if (param instanceof ParametersWithRandom)
         {
             ParametersWithRandom  rParam = (ParametersWithRandom)param;
 
             this.random = rParam.getRandom();
-            kParam = (AsymmetricKeyParameter)rParam.getParameters();
         }
         else
         {   
             this.random = new SecureRandom();
-            kParam = (AsymmetricKeyParameter)param;
         }
 
-        engine.init(forEncryption, kParam);
+        engine.init(forEncryption, param);
 
         this.forEncryption = forEncryption;
     }
@@ -200,7 +206,7 @@
         throws InvalidCipherTextException
     {
         byte[]  data = engine.processBlock(in, inOff, inLen);
-        byte[]  block = null;
+        byte[]  block;
 
         //
         // as we may have zeros in our leading bytes for the block we produced
@@ -262,7 +268,7 @@
 
         for (start = 2 * defHash.length; start != block.length; start++)
         {
-            if (block[start] == 1 || block[start] != 0)
+            if (block[start] != 0)
             {
                 break;
             }
@@ -308,7 +314,7 @@
         int     length)
     {
         byte[]  mask = new byte[length];
-        byte[]  hashBuf = new byte[defHash.length];
+        byte[]  hashBuf = new byte[mgf1Hash.getDigestSize()];
         byte[]  C = new byte[4];
         int     counter = 0;
 
@@ -318,23 +324,23 @@
         {
             ItoOSP(counter, C);
 
-            hash.update(Z, zOff, zLen);
-            hash.update(C, 0, C.length);
-            hash.doFinal(hashBuf, 0);
+            mgf1Hash.update(Z, zOff, zLen);
+            mgf1Hash.update(C, 0, C.length);
+            mgf1Hash.doFinal(hashBuf, 0);
 
-            System.arraycopy(hashBuf, 0, mask, counter * defHash.length, defHash.length);
+            System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
         }
-        while (++counter < (length / defHash.length));
+        while (++counter < (length / hashBuf.length));
 
-        if ((counter * defHash.length) < length)
+        if ((counter * hashBuf.length) < length)
         {
             ItoOSP(counter, C);
 
-            hash.update(Z, zOff, zLen);
-            hash.update(C, 0, C.length);
-            hash.doFinal(hashBuf, 0);
+            mgf1Hash.update(Z, zOff, zLen);
+            mgf1Hash.update(C, 0, C.length);
+            mgf1Hash.doFinal(hashBuf, 0);
 
-            System.arraycopy(hashBuf, 0, mask, counter * defHash.length, mask.length - (counter * defHash.length));
+            System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
         }
 
         return mask;
diff --git a/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index 09b32eb..a6a5986 100644
--- a/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.encodings;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.security.SecureRandom;
 
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
@@ -23,9 +25,9 @@
      * true by default.
      * </p>
      */
-    public static String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
+    public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
     
-    private static int      HEADER_LENGTH = 10;
+    private static final int HEADER_LENGTH = 10;
 
     private SecureRandom            random;
     private AsymmetricBlockCipher   engine;
@@ -41,9 +43,26 @@
         AsymmetricBlockCipher   cipher)
     {
         this.engine = cipher;
-        this.useStrictLength = System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY, "true").equals("true");
+        this.useStrictLength = useStrict();
     }   
 
+    //
+    // for J2ME compatibility
+    //
+    private boolean useStrict()
+    {
+        // required if security manager has been installed.
+        String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
+        {
+            public Object run()
+            {
+                return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
+            }
+        });
+
+        return strict == null || strict.equals("true");
+    }
+
     public AsymmetricBlockCipher getUnderlyingCipher()
     {
         return engine;
@@ -68,7 +87,7 @@
             kParam = (AsymmetricKeyParameter)param;
         }
 
-        engine.init(forEncryption, kParam);
+        engine.init(forEncryption, param);
 
         this.forPrivateKey = kParam.isPrivate();
         this.forEncryption = forEncryption;
@@ -124,6 +143,11 @@
         int     inLen)
         throws InvalidCipherTextException
     {
+        if (inLen > getInputBlockSize())
+        {
+            throw new IllegalArgumentException("input data too large");
+        }
+        
         byte[]  block = new byte[engine.getInputBlockSize()];
 
         if (forPrivateKey)
@@ -209,7 +233,7 @@
 
         start++;           // data should start at the next byte
 
-        if (start >= block.length || start < HEADER_LENGTH)
+        if (start > block.length || start < HEADER_LENGTH)
         {
             throw new InvalidCipherTextException("no data in block");
         }
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
index 908e78f..d9bb482 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
@@ -417,7 +417,7 @@
     {
     }
 
-    private final void unpackBlock(
+    private void unpackBlock(
         byte[]      bytes,
         int         off)
     {
@@ -444,7 +444,7 @@
         C3 |= bytes[index++] << 24;
     }
 
-    private final void packBlock(
+    private void packBlock(
         byte[]      bytes,
         int         off)
     {
@@ -472,7 +472,7 @@
     }
 
 
-    private final void encryptBlock(int[][] KW)
+    private void encryptBlock(int[][] KW)
     {
         int r, r0, r1, r2, r3;
 
@@ -509,7 +509,7 @@
 
     }
 
-    private final void decryptBlock(int[][] KW)
+    private void decryptBlock(int[][] KW)
     {
         int r, r0, r1, r2, r3;
 
@@ -535,7 +535,7 @@
         r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0];
         r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1];
         r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2];
-        r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r--][3];
+        r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r][3];
         
         // the final round's table is a simple function of Si so we don't use a whole other four tables for it
 
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java b/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
index e693a1b..2374be1 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -329,7 +329,7 @@
      0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 
      0x2c3a1616};
 
-private static final int[] Tinv0 =
+    private static final int[] Tinv0 =
     {
      0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 
      0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 
@@ -748,7 +748,7 @@
     {
     }
 
-    private final void unpackBlock(
+    private void unpackBlock(
         byte[]      bytes,
         int         off)
     {
@@ -775,7 +775,7 @@
         C3 |= bytes[index++] << 24;
     }
 
-    private final void packBlock(
+    private void packBlock(
         byte[]      bytes,
         int         off)
     {
@@ -802,7 +802,7 @@
         bytes[index++] = (byte)(C3 >> 24);
     }
 
-    private final void encryptBlock(int[][] KW)
+    private void encryptBlock(int[][] KW)
     {
         int r, r0, r1, r2, r3;
         
@@ -838,7 +838,7 @@
 
     }
 
-    private final void decryptBlock(int[][] KW)
+    private void decryptBlock(int[][] KW)
     {
         int r0, r1, r2, r3;
 
@@ -864,7 +864,7 @@
         r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0];
         r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1];
         r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2];
-        r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r--][3];
+        r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r][3];
         
         // the final round's table is a simple function of Si so we don't use a whole other four tables for it
 
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java b/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
deleted file mode 100644
index b0730e3..0000000
--- a/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
+++ /dev/null
@@ -1,440 +0,0 @@
-package org.bouncycastle.crypto.engines;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * an implementation of the AES (Rijndael), from FIPS-197.
- * <p>
- * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
- *
- * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
- *
- * There are three levels of tradeoff of speed vs memory
- * Because java has no preprocessor, they are written as three separate classes from which to choose
- *
- * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
- * and 4 for decryption.
- *
- * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
- * adding 12 rotate operations per round to compute the values contained in the other tables from
- * the contents of the first
- *
- * The slowest version uses no static tables at all and computes the values
- * in each round.
- * <p>
- * This file contains the slowest performance version with no static tables
- * for round precomputation, but it has the smallest foot print.
- *
- */
-public class AESLightEngine
-    implements BlockCipher
-{
-    // The S box
-    private static final byte[] S = {
-        (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
-        (byte)48,   (byte)1, (byte)103,  (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
-        (byte)202, (byte)130, (byte)201, (byte)125, (byte)250,  (byte)89,  (byte)71, (byte)240,
-        (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
-        (byte)183, (byte)253, (byte)147,  (byte)38,  (byte)54,  (byte)63, (byte)247, (byte)204,
-        (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216,  (byte)49,  (byte)21,
-        (byte)4, (byte)199,  (byte)35, (byte)195,  (byte)24, (byte)150,   (byte)5, (byte)154,
-        (byte)7,  (byte)18, (byte)128, (byte)226, (byte)235,  (byte)39, (byte)178, (byte)117,
-        (byte)9, (byte)131,  (byte)44,  (byte)26,  (byte)27, (byte)110,  (byte)90, (byte)160,
-        (byte)82,  (byte)59, (byte)214, (byte)179,  (byte)41, (byte)227,  (byte)47, (byte)132,
-        (byte)83, (byte)209,   (byte)0, (byte)237,  (byte)32, (byte)252, (byte)177,  (byte)91,
-        (byte)106, (byte)203, (byte)190,  (byte)57,  (byte)74,  (byte)76,  (byte)88, (byte)207,
-        (byte)208, (byte)239, (byte)170, (byte)251,  (byte)67,  (byte)77,  (byte)51, (byte)133,
-        (byte)69, (byte)249,   (byte)2, (byte)127,  (byte)80,  (byte)60, (byte)159, (byte)168,
-        (byte)81, (byte)163,  (byte)64, (byte)143, (byte)146, (byte)157,  (byte)56, (byte)245,
-        (byte)188, (byte)182, (byte)218,  (byte)33,  (byte)16, (byte)255, (byte)243, (byte)210,
-        (byte)205,  (byte)12,  (byte)19, (byte)236,  (byte)95, (byte)151,  (byte)68,  (byte)23,
-        (byte)196, (byte)167, (byte)126,  (byte)61, (byte)100,  (byte)93,  (byte)25, (byte)115,
-        (byte)96, (byte)129,  (byte)79, (byte)220,  (byte)34,  (byte)42, (byte)144, (byte)136,
-        (byte)70, (byte)238, (byte)184,  (byte)20, (byte)222,  (byte)94,  (byte)11, (byte)219,
-        (byte)224,  (byte)50,  (byte)58,  (byte)10,  (byte)73,   (byte)6,  (byte)36,  (byte)92,
-        (byte)194, (byte)211, (byte)172,  (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
-        (byte)231, (byte)200,  (byte)55, (byte)109, (byte)141, (byte)213,  (byte)78, (byte)169,
-        (byte)108,  (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174,   (byte)8,
-        (byte)186, (byte)120,  (byte)37,  (byte)46,  (byte)28, (byte)166, (byte)180, (byte)198,
-        (byte)232, (byte)221, (byte)116,  (byte)31,  (byte)75, (byte)189, (byte)139, (byte)138,
-        (byte)112,  (byte)62, (byte)181, (byte)102,  (byte)72,   (byte)3, (byte)246,  (byte)14,
-        (byte)97,  (byte)53,  (byte)87, (byte)185, (byte)134, (byte)193,  (byte)29, (byte)158,
-        (byte)225, (byte)248, (byte)152,  (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
-        (byte)155,  (byte)30, (byte)135, (byte)233, (byte)206,  (byte)85,  (byte)40, (byte)223,
-        (byte)140, (byte)161, (byte)137,  (byte)13, (byte)191, (byte)230,  (byte)66, (byte)104,
-        (byte)65, (byte)153,  (byte)45,  (byte)15, (byte)176,  (byte)84, (byte)187,  (byte)22,
-    };
-
-    // The inverse S-box
-    private static final byte[] Si = {
-        (byte)82,   (byte)9, (byte)106, (byte)213,  (byte)48,  (byte)54, (byte)165,  (byte)56,
-        (byte)191,  (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
-        (byte)124, (byte)227,  (byte)57, (byte)130, (byte)155,  (byte)47, (byte)255, (byte)135,
-        (byte)52, (byte)142,  (byte)67,  (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
-        (byte)84, (byte)123, (byte)148,  (byte)50, (byte)166, (byte)194,  (byte)35,  (byte)61,
-        (byte)238,  (byte)76, (byte)149,  (byte)11,  (byte)66, (byte)250, (byte)195,  (byte)78,
-        (byte)8,  (byte)46, (byte)161, (byte)102,  (byte)40, (byte)217,  (byte)36, (byte)178,
-        (byte)118,  (byte)91, (byte)162,  (byte)73, (byte)109, (byte)139, (byte)209,  (byte)37,
-        (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152,  (byte)22,
-        (byte)212, (byte)164,  (byte)92, (byte)204,  (byte)93, (byte)101, (byte)182, (byte)146,
-        (byte)108, (byte)112,  (byte)72,  (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
-        (byte)94,  (byte)21,  (byte)70,  (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
-        (byte)144, (byte)216, (byte)171,   (byte)0, (byte)140, (byte)188, (byte)211,  (byte)10,
-        (byte)247, (byte)228,  (byte)88,   (byte)5, (byte)184, (byte)179,  (byte)69,   (byte)6,
-        (byte)208,  (byte)44,  (byte)30, (byte)143, (byte)202,  (byte)63,  (byte)15,   (byte)2,
-        (byte)193, (byte)175, (byte)189,   (byte)3,   (byte)1,  (byte)19, (byte)138, (byte)107,
-        (byte)58, (byte)145,  (byte)17,  (byte)65,  (byte)79, (byte)103, (byte)220, (byte)234,
-        (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
-        (byte)150, (byte)172, (byte)116,  (byte)34, (byte)231, (byte)173,  (byte)53, (byte)133,
-        (byte)226, (byte)249,  (byte)55, (byte)232,  (byte)28, (byte)117, (byte)223, (byte)110,
-        (byte)71, (byte)241,  (byte)26, (byte)113,  (byte)29,  (byte)41, (byte)197, (byte)137,
-        (byte)111, (byte)183,  (byte)98,  (byte)14, (byte)170,  (byte)24, (byte)190,  (byte)27,
-        (byte)252,  (byte)86,  (byte)62,  (byte)75, (byte)198, (byte)210, (byte)121,  (byte)32,
-        (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205,  (byte)90, (byte)244,
-        (byte)31, (byte)221, (byte)168,  (byte)51, (byte)136,   (byte)7, (byte)199,  (byte)49,
-        (byte)177,  (byte)18,  (byte)16,  (byte)89,  (byte)39, (byte)128, (byte)236,  (byte)95,
-        (byte)96,  (byte)81, (byte)127, (byte)169,  (byte)25, (byte)181,  (byte)74,  (byte)13,
-        (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
-        (byte)160, (byte)224,  (byte)59,  (byte)77, (byte)174,  (byte)42, (byte)245, (byte)176,
-        (byte)200, (byte)235, (byte)187,  (byte)60, (byte)131,  (byte)83, (byte)153,  (byte)97,
-        (byte)23,  (byte)43,   (byte)4, (byte)126, (byte)186, (byte)119, (byte)214,  (byte)38,
-        (byte)225, (byte)105,  (byte)20,  (byte)99,  (byte)85,  (byte)33,  (byte)12, (byte)125,
-        };
-
-    // vector used in calculating key schedule (powers of x in GF(256))
-    private static final int[] rcon = {
-         0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
-         0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
-
-    private int shift(
-        int     r,
-        int     shift)
-    {
-        return (r >>> shift) | (r << -shift);
-    }
-
-    /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
-
-    private static final int m1 = 0x80808080;
-    private static final int m2 = 0x7f7f7f7f;
-    private static final int m3 = 0x0000001b;
-
-    private int FFmulX(int x)
-    {
-        return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
-    }
-
-    /* 
-       The following defines provide alternative definitions of FFmulX that might
-       give improved performance if a fast 32-bit multiply is not available.
-       
-       private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } 
-       private static final int  m4 = 0x1b1b1b1b;
-       private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } 
-
-    */
-
-    private int mcol(int x)
-    {
-        int f2 = FFmulX(x);
-        return f2 ^ shift(x ^ f2, 8) ^ shift(x, 16) ^ shift(x, 24);
-    }
-
-    private int inv_mcol(int x)
-    {
-        int f2 = FFmulX(x);
-        int f4 = FFmulX(f2);
-        int f8 = FFmulX(f4);
-        int f9 = x ^ f8;
-        
-        return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
-    }
-
-
-    private int subWord(int x)
-    {
-        return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
-    }
-
-    /**
-     * Calculate the necessary round keys
-     * The number of calculations depends on key size and block size
-     * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
-     * This code is written assuming those are the only possible values
-     */
-    private int[][] generateWorkingKey(
-                                    byte[] key,
-                                    boolean forEncryption)
-    {
-        int         KC = key.length / 4;  // key length in words
-        int         t;
-        
-        if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
-        {
-            throw new IllegalArgumentException("Key length not 128/192/256 bits.");
-        }
-
-        ROUNDS = KC + 6;  // This is not always true for the generalized Rijndael that allows larger block sizes
-        int[][] W = new int[ROUNDS+1][4];   // 4 words in a block
-        
-        //
-        // copy the key into the round key array
-        //
-        
-        t = 0;
-        int i = 0;
-        while (i < key.length)
-            {
-                W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
-                i+=4;
-                t++;
-            }
-        
-        //
-        // while not enough round key material calculated
-        // calculate new values
-        //
-        int k = (ROUNDS + 1) << 2;
-        for (i = KC; (i < k); i++)
-            {
-                int temp = W[(i-1)>>2][(i-1)&3];
-                if ((i % KC) == 0)
-                {
-                    temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1];
-                }
-                else if ((KC > 6) && ((i % KC) == 4))
-                {
-                    temp = subWord(temp);
-                }
-                
-                W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
-            }
-
-        if (!forEncryption)
-        {
-            for (int j = 1; j < ROUNDS; j++)
-            {
-                for (i = 0; i < 4; i++) 
-                {
-                    W[j][i] = inv_mcol(W[j][i]);
-                }
-            }
-        }
-
-        return W;
-    }
-
-    private int         ROUNDS;
-    private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
-    private boolean     forEncryption;
-
-    private static final int BLOCK_SIZE = 16;
-
-    /**
-     * default constructor - 128 bit block size.
-     */
-    public AESLightEngine()
-    {
-    }
-
-    /**
-     * initialise an AES cipher.
-     *
-     * @param forEncryption whether or not we are for encryption.
-     * @param params the parameters required to set up the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
-     */
-    public void init(
-        boolean           forEncryption,
-        CipherParameters  params)
-    {
-        if (params instanceof KeyParameter)
-        {
-            WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
-            this.forEncryption = forEncryption;
-            return;
-        }
-
-        throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
-    }
-
-    public String getAlgorithmName()
-    {
-        return "AES";
-    }
-
-    public int getBlockSize()
-    {
-        return BLOCK_SIZE;
-    }
-
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
-    {
-        if (WorkingKey == null)
-        {
-            throw new IllegalStateException("AES engine not initialised");
-        }
-
-        if ((inOff + (32 / 2)) > in.length)
-        {
-            throw new DataLengthException("input buffer too short");
-        }
-
-        if ((outOff + (32 / 2)) > out.length)
-        {
-            throw new DataLengthException("output buffer too short");
-        }
-
-        if (forEncryption)
-        {
-            unpackBlock(in, inOff);
-            encryptBlock(WorkingKey);
-            packBlock(out, outOff);
-        }
-        else
-        {
-            unpackBlock(in, inOff);
-            decryptBlock(WorkingKey);
-            packBlock(out, outOff);
-        }
-
-        return BLOCK_SIZE;
-    }
-
-    public void reset()
-    {
-    }
-
-    private final void unpackBlock(
-        byte[]      bytes,
-        int         off)
-    {
-        int     index = off;
-
-        C0 = (bytes[index++] & 0xff);
-        C0 |= (bytes[index++] & 0xff) << 8;
-        C0 |= (bytes[index++] & 0xff) << 16;
-        C0 |= bytes[index++] << 24;
-
-        C1 = (bytes[index++] & 0xff);
-        C1 |= (bytes[index++] & 0xff) << 8;
-        C1 |= (bytes[index++] & 0xff) << 16;
-        C1 |= bytes[index++] << 24;
-
-        C2 = (bytes[index++] & 0xff);
-        C2 |= (bytes[index++] & 0xff) << 8;
-        C2 |= (bytes[index++] & 0xff) << 16;
-        C2 |= bytes[index++] << 24;
-
-        C3 = (bytes[index++] & 0xff);
-        C3 |= (bytes[index++] & 0xff) << 8;
-        C3 |= (bytes[index++] & 0xff) << 16;
-        C3 |= bytes[index++] << 24;
-    }
-
-    private final void packBlock(
-        byte[]      bytes,
-        int         off)
-    {
-        int     index = off;
-
-        bytes[index++] = (byte)C0;
-        bytes[index++] = (byte)(C0 >> 8);
-        bytes[index++] = (byte)(C0 >> 16);
-        bytes[index++] = (byte)(C0 >> 24);
-
-        bytes[index++] = (byte)C1;
-        bytes[index++] = (byte)(C1 >> 8);
-        bytes[index++] = (byte)(C1 >> 16);
-        bytes[index++] = (byte)(C1 >> 24);
-
-        bytes[index++] = (byte)C2;
-        bytes[index++] = (byte)(C2 >> 8);
-        bytes[index++] = (byte)(C2 >> 16);
-        bytes[index++] = (byte)(C2 >> 24);
-
-        bytes[index++] = (byte)C3;
-        bytes[index++] = (byte)(C3 >> 8);
-        bytes[index++] = (byte)(C3 >> 16);
-        bytes[index++] = (byte)(C3 >> 24);
-    }
-
-    private void encryptBlock(int[][] KW)
-    {
-        int r, r0, r1, r2, r3;
-
-        C0 ^= KW[0][0];
-        C1 ^= KW[0][1];
-        C2 ^= KW[0][2];
-        C3 ^= KW[0][3];
-
-        for (r = 1; r < ROUNDS - 1;)
-        {
-            r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
-            r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
-            r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
-            r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
-            C0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
-            C1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1];
-            C2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2];
-            C3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3];
-        }
-
-        r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
-        r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
-        r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
-        r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
-
-        // the final round is a simple function of S
-
-        C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
-        C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
-        C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
-        C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
-
-    }
-
-    private final void decryptBlock(int[][] KW)
-    {
-        int r, r0, r1, r2, r3;
-
-        C0 ^= KW[ROUNDS][0];
-        C1 ^= KW[ROUNDS][1];
-        C2 ^= KW[ROUNDS][2];
-        C3 ^= KW[ROUNDS][3];
-
-        for (r = ROUNDS-1; r>1;)
-        {
-            r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
-            r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
-            r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
-            r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--][3];
-            C0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0];
-            C1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1];
-            C2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
-            C3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3];
-        }
-
-        r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
-        r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
-        r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
-        r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--][3];
-
-        // the final round's table is a simple function of Si
-
-        C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
-        C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
-        C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
-        C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java b/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
index 6f10eff..5d316ac 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -1,13 +1,5 @@
 package org.bouncycastle.crypto.engines;
 
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.Wrapper;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-
 /**
  * an implementation of the AES Key Wrapper from the NIST Key Wrap
  * Specification.
@@ -15,153 +7,10 @@
  * For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
  */
 public class AESWrapEngine
-    implements Wrapper
+    extends RFC3394WrapEngine
 {
-    private BlockCipher     engine = new AESEngine();
-    private KeyParameter    param;
-    private boolean         forWrapping;
-
-    private byte[]          iv = {
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
-
-    public void init(
-        boolean             forWrapping,
-        CipherParameters    param)
+    public AESWrapEngine()
     {
-        this.forWrapping = forWrapping;
-
-        if (param instanceof KeyParameter)
-        {
-            this.param = (KeyParameter)param;
-        }
-        else if (param instanceof ParametersWithIV)
-        {
-            this.iv = ((ParametersWithIV) param).getIV();
-            this.param = (KeyParameter) ((ParametersWithIV) param).getParameters();
-            if (this.iv.length != 8)
-            {
-               throw new IllegalArgumentException("IV not multiple of 8");
-            }
-        }
-    }
-
-    public String getAlgorithmName()
-    {
-        return "AES";
-    }
-
-    public byte[] wrap(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
-    {
-        if (!forWrapping)
-        {
-            throw new IllegalStateException("not set for wrapping");
-        }
-
-        int     n = inLen / 8;
-
-        if ((n * 8) != inLen)
-        {
-            throw new DataLengthException("wrap data must be a multiple of 8 bytes");
-        }
-
-        byte[]  block = new byte[inLen + iv.length];
-        byte[]  buf = new byte[8 + iv.length];
-
-        System.arraycopy(iv, 0, block, 0, iv.length);
-        System.arraycopy(in, 0, block, iv.length, inLen);
-
-        engine.init(true, param);
-
-        for (int j = 0; j != 6; j++)
-        {
-            for (int i = 1; i <= n; i++)
-            {
-                System.arraycopy(block, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * i, buf, iv.length, 8);
-                engine.processBlock(buf, 0, buf, 0);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
-                {
-                    byte    v = (byte)t;
-
-                    buf[iv.length - k] ^= v;
-
-                    t >>>= 8;
-                }
-
-                System.arraycopy(buf, 0, block, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * i, 8);
-            }
-        }
-
-        return block;
-    }
-
-    public byte[] unwrap(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
-        throws InvalidCipherTextException
-    {
-        if (forWrapping)
-        {
-            throw new IllegalStateException("not set for unwrapping");
-        }
-
-        int     n = inLen / 8;
-
-        if ((n * 8) != inLen)
-        {
-            throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
-        }
-
-        byte[]  block = new byte[inLen - iv.length];
-        byte[]  a = new byte[iv.length];
-        byte[]  buf = new byte[8 + iv.length];
-
-        System.arraycopy(in, 0, a, 0, iv.length);
-        System.arraycopy(in, iv.length, block, 0, inLen - iv.length);
-
-        engine.init(false, param);
-
-        n = n - 1;
-
-        for (int j = 5; j >= 0; j--)
-        {
-            for (int i = n; i >= 1; i--)
-            {
-                System.arraycopy(a, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
-                {
-                    byte    v = (byte)t;
-
-                    buf[iv.length - k] ^= v;
-
-                    t >>>= 8;
-                }
-
-                engine.processBlock(buf, 0, buf, 0);
-                System.arraycopy(buf, 0, a, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
-            }
-        }
-
-        for (int i = 0; i != iv.length; i++)
-        {
-            if (a[i] != iv[i])
-            {
-                throw new InvalidCipherTextException("checksum failed");
-            }
-        }
-
-        return block;
+        super(new AESEngine());
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
index b795a9e..b04911c 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
@@ -96,19 +96,19 @@
      * Outerbridge's D3DES...
      */
 
-    static short[]    Df_Key =
-        {
-            0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
-            0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
-            0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
-        };
+//    private static final short[]    Df_Key =
+//        {
+//            0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+//            0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+//            0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+//        };
 
-    static short[]    bytebit =
+    private static final short[]    bytebit =
         {
             0200, 0100, 040, 020, 010, 04, 02, 01
         };
 
-    static int[]    bigbyte =
+    private static final int[]    bigbyte =
         {
             0x800000, 0x400000, 0x200000, 0x100000,
             0x80000,  0x40000,  0x20000,  0x10000,
@@ -122,7 +122,7 @@
      * Use the key schedule specified in the Standard (ANSI X3.92-1981).
      */
 
-    static byte[]    pc1 =
+    private static final byte[]    pc1 =
         {
             56, 48, 40, 32, 24, 16,  8,   0, 57, 49, 41, 33, 25, 17,
              9,  1, 58, 50, 42, 34, 26,  18, 10,  2, 59, 51, 43, 35,
@@ -130,13 +130,13 @@
             13,  5, 60, 52, 44, 36, 28,  20, 12,  4, 27, 19, 11,  3
         };
 
-    static byte[] totrot =
+    private static final byte[] totrot =
         {
             1, 2, 4, 6, 8, 10, 12, 14,
             15, 17, 19, 21, 23, 25, 27, 28
         };
 
-    static byte[] pc2 =
+    private static final byte[] pc2 =
         {
             13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
             22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
@@ -144,7 +144,7 @@
             43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
         };
 
-    static int[] SP1 = {
+    private static final int[] SP1 = {
         0x01010400, 0x00000000, 0x00010000, 0x01010404,
         0x01010004, 0x00010404, 0x00000004, 0x00010000,
         0x00000400, 0x01010400, 0x01010404, 0x00000400,
@@ -163,7 +163,7 @@
         0x00010004, 0x00010400, 0x00000000, 0x01010004
     };
 
-    static int[] SP2 = {
+    private static final int[] SP2 = {
         0x80108020, 0x80008000, 0x00008000, 0x00108020,
         0x00100000, 0x00000020, 0x80100020, 0x80008020,
         0x80000020, 0x80108020, 0x80108000, 0x80000000,
@@ -182,7 +182,7 @@
         0x80000000, 0x80100020, 0x80108020, 0x00108000
     };
 
-    static int[] SP3 = {
+    private static final int[] SP3 = {
         0x00000208, 0x08020200, 0x00000000, 0x08020008,
         0x08000200, 0x00000000, 0x00020208, 0x08000200,
         0x00020008, 0x08000008, 0x08000008, 0x00020000,
@@ -201,7 +201,7 @@
         0x00020208, 0x00000008, 0x08020008, 0x00020200
     };
 
-    static int[] SP4 = {
+    private static final int[] SP4 = {
         0x00802001, 0x00002081, 0x00002081, 0x00000080,
         0x00802080, 0x00800081, 0x00800001, 0x00002001,
         0x00000000, 0x00802000, 0x00802000, 0x00802081,
@@ -220,7 +220,7 @@
         0x00000080, 0x00800000, 0x00002000, 0x00802080
     };
 
-    static int[] SP5 = {
+    private static final int[] SP5 = {
         0x00000100, 0x02080100, 0x02080000, 0x42000100,
         0x00080000, 0x00000100, 0x40000000, 0x02080000,
         0x40080100, 0x00080000, 0x02000100, 0x40080100,
@@ -239,7 +239,7 @@
         0x00000000, 0x40080000, 0x02080100, 0x40000100
     };
 
-    static int[] SP6 = {
+    private static final int[] SP6 = {
         0x20000010, 0x20400000, 0x00004000, 0x20404010,
         0x20400000, 0x00000010, 0x20404010, 0x00400000,
         0x20004000, 0x00404010, 0x00400000, 0x20000010,
@@ -258,7 +258,7 @@
         0x20404000, 0x20000000, 0x00400010, 0x20004010
     };
 
-    static int[] SP7 = {
+    private static final int[] SP7 = {
         0x00200000, 0x04200002, 0x04000802, 0x00000000,
         0x00000800, 0x04000802, 0x00200802, 0x04200800,
         0x04200802, 0x00200000, 0x00000000, 0x04000002,
@@ -277,7 +277,7 @@
         0x04000002, 0x04000800, 0x00000800, 0x00200002
     };
 
-    static int[] SP8 = {
+    private static final int[] SP8 = {
         0x10001040, 0x00001000, 0x00040000, 0x10041040,
         0x10000000, 0x10001040, 0x00000040, 0x10000000,
         0x00040040, 0x10040000, 0x10041040, 0x00041000,
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java b/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
index a340034..f915434 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
@@ -42,33 +42,31 @@
             throw new IllegalArgumentException("invalid parameter passed to DESede init - " + params.getClass().getName());
         }
 
-        byte[]      keyMaster = ((KeyParameter)params).getKey();
-        byte[]      key1 = new byte[8], key2 = new byte[8], key3 = new byte[8];
+        byte[] keyMaster = ((KeyParameter)params).getKey();
 
         if (keyMaster.length > 24)
         {
             throw new IllegalArgumentException("key size greater than 24 bytes");
         }
-        
+
         this.forEncryption = encrypting;
 
+        byte[] key1 = new byte[8];
+        System.arraycopy(keyMaster, 0, key1, 0, key1.length);
+        workingKey1 = generateWorkingKey(encrypting, key1);
+
+        byte[] key2 = new byte[8];
+        System.arraycopy(keyMaster, 8, key2, 0, key2.length);
+        workingKey2 = generateWorkingKey(!encrypting, key2);
+
         if (keyMaster.length == 24)
         {
-            System.arraycopy(keyMaster, 0, key1, 0, key1.length);
-            System.arraycopy(keyMaster, 8, key2, 0, key2.length);
+            byte[] key3 = new byte[8];
             System.arraycopy(keyMaster, 16, key3, 0, key3.length);
-
-            workingKey1 = generateWorkingKey(encrypting, key1);
-            workingKey2 = generateWorkingKey(!encrypting, key2);
             workingKey3 = generateWorkingKey(encrypting, key3);
         }
         else    // 16 byte key
         {
-            System.arraycopy(keyMaster, 0, key1, 0, key1.length);
-            System.arraycopy(keyMaster, 8, key2, 0, key2.length);
-
-            workingKey1 = generateWorkingKey(encrypting, key1);
-            workingKey2 = generateWorkingKey(!encrypting, key2);
             workingKey3 = workingKey1;
         }
     }
@@ -104,17 +102,19 @@
             throw new DataLengthException("output buffer too short");
         }
 
+        byte[] temp = new byte[BLOCK_SIZE];
+
         if (forEncryption)
         {
-            desFunc(workingKey1, in, inOff, out, outOff);
-            desFunc(workingKey2, out, outOff, out, outOff);
-            desFunc(workingKey3, out, outOff, out, outOff);
+            desFunc(workingKey1, in, inOff, temp, 0);
+            desFunc(workingKey2, temp, 0, temp, 0);
+            desFunc(workingKey3, temp, 0, out, outOff);
         }
         else
         {
-            desFunc(workingKey3, in, inOff, out, outOff);
-            desFunc(workingKey2, out, outOff, out, outOff);
-            desFunc(workingKey1, out, outOff, out, outOff);
+            desFunc(workingKey3, in, inOff, temp, 0);
+            desFunc(workingKey2, temp, 0, temp, 0);
+            desFunc(workingKey1, temp, 0, out, outOff);
         }
 
         return BLOCK_SIZE;
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index e37244b..a3c72cc 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -10,6 +10,8 @@
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Wrap keys according to
@@ -65,6 +67,18 @@
         this.forWrapping = forWrapping;
         this.engine = new CBCBlockCipher(new DESedeEngine());
 
+        SecureRandom sr;
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom pr = (ParametersWithRandom) param;
+            param = pr.getParameters();
+            sr = pr.getRandom();
+        }
+        else
+        {
+            sr = new SecureRandom();
+        }
+
         if (param instanceof KeyParameter)
         {
             this.param = (KeyParameter)param;
@@ -75,9 +89,6 @@
                 // Hm, we have no IV but we want to wrap ?!?
                 // well, then we have to create our own IV.
                 this.iv = new byte[8];
-
-                SecureRandom sr = new SecureRandom();
-
                 sr.nextBytes(iv);
 
                 this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
@@ -144,40 +155,31 @@
 
       // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
       // initialization vector. Call the results TEMP1.
-      byte TEMP1[] = new byte[WKCKS.length];
 
-      System.arraycopy(WKCKS, 0, TEMP1, 0, WKCKS.length);
+      int blockSize = engine.getBlockSize();
 
-      int noOfBlocks = WKCKS.length / engine.getBlockSize();
-      int extraBytes = WKCKS.length % engine.getBlockSize();
-
-      if (extraBytes != 0) 
+      if (WKCKS.length % blockSize != 0) 
       {
          throw new IllegalStateException("Not multiple of block length");
       }
 
       engine.init(true, paramPlusIV);
 
-      for (int i = 0; i < noOfBlocks; i++) 
-      {
-         int currentBytePos = i * engine.getBlockSize();
+      byte TEMP1[] = new byte[WKCKS.length];
 
-         engine.processBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
+      for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize) 
+      {
+         engine.processBlock(WKCKS, currentBytePos, TEMP1, currentBytePos);
       }
 
-      // Left TEMP2 = IV || TEMP1.
+      // Let TEMP2 = IV || TEMP1.
       byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
 
       System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
       System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
 
       // Reverse the order of the octets in TEMP2 and call the result TEMP3.
-      byte[] TEMP3 = new byte[TEMP2.length];
-
-      for (int i = 0; i < TEMP2.length; i++) 
-      {
-         TEMP3[i] = TEMP2[TEMP2.length - (i + 1)];
-      }
+      byte[] TEMP3 = reverse(TEMP2);
 
       // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
       // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
@@ -186,10 +188,8 @@
 
       this.engine.init(true, param2);
 
-      for (int i = 0; i < noOfBlocks + 1; i++) 
+      for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize) 
       {
-         int currentBytePos = i * engine.getBlockSize();
-
          engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
       }
 
@@ -217,11 +217,11 @@
         {
             throw new InvalidCipherTextException("Null pointer as ciphertext");
         }
-        
-        if (inLen % engine.getBlockSize() != 0)
+
+        final int blockSize = engine.getBlockSize();
+        if (inLen % blockSize != 0)
         {
-            throw new InvalidCipherTextException("Ciphertext not multiple of "
-                    + engine.getBlockSize());
+            throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize);
         }
 
       /*
@@ -248,22 +248,13 @@
 
       byte TEMP3[] = new byte[inLen];
 
-      System.arraycopy(in, inOff, TEMP3, 0, inLen);
-
-      for (int i = 0; i < (TEMP3.length / engine.getBlockSize()); i++) 
+      for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize) 
       {
-         int currentBytePos = i * engine.getBlockSize();
-
-         engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
       }
 
       // Reverse the order of the octets in TEMP3 and call the result TEMP2.
-      byte[] TEMP2 = new byte[TEMP3.length];
-
-      for (int i = 0; i < TEMP3.length; i++) 
-      {
-         TEMP2[i] = TEMP3[TEMP3.length - (i + 1)];
-      }
+      byte[] TEMP2 = reverse(TEMP3);
 
       // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
       this.iv = new byte[8];
@@ -281,13 +272,9 @@
 
       byte[] WKCKS = new byte[TEMP1.length];
 
-      System.arraycopy(TEMP1, 0, WKCKS, 0, TEMP1.length);
-
-      for (int i = 0; i < (WKCKS.length / engine.getBlockSize()); i++) 
+      for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize) 
       {
-         int currentBytePos = i * engine.getBlockSize();
-
-         engine.processBlock(WKCKS, currentBytePos, WKCKS, currentBytePos);
+         engine.processBlock(TEMP1, currentBytePos, WKCKS, currentBytePos);
       }
 
       // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
@@ -346,21 +333,16 @@
         byte[] key,
         byte[] checksum)
     {
-        byte[] calculatedChecksum = calculateCMSKeyChecksum(key);
+        return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+    }
 
-        if (checksum.length != calculatedChecksum.length)
+    private static byte[] reverse(byte[] bs)
+    {
+        byte[] result = new byte[bs.length];
+        for (int i = 0; i < bs.length; i++) 
         {
-            return false;
+           result[i] = bs[bs.length - (i + 1)];
         }
-
-        for (int i = 0; i != checksum.length; i++)
-        {
-            if (checksum[i] != calculatedChecksum[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return result;
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java b/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
deleted file mode 100644
index 723fd9a..0000000
--- a/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
+++ /dev/null
@@ -1,245 +0,0 @@
-package org.bouncycastle.crypto.engines;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.crypto.BasicAgreement;
-import org.bouncycastle.crypto.BufferedBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.params.IESParameters;
-import org.bouncycastle.crypto.params.IESWithCipherParameters;
-import org.bouncycastle.crypto.params.KDFParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * support class for constructing intergrated encryption ciphers
- * for doing basic message exchanges on top of key agreement ciphers
- */
-public class IESEngine
-{
-    BasicAgreement      agree;
-    DerivationFunction  kdf;
-    Mac                 mac;
-    BufferedBlockCipher cipher;
-    byte[]              macBuf;
-
-    boolean             forEncryption;
-    CipherParameters    privParam, pubParam;
-    IESParameters       param;
-
-    /**
-     * set up for use with stream mode, where the key derivation function
-     * is used to provide a stream of bytes to xor with the message.
-     *
-     * @param agree the key agreement used as the basis for the encryption
-     * @param kdf the key derivation function used for byte generation
-     * @param mac the message authentication code generator for the message
-     */
-    public IESEngine(
-        BasicAgreement      agree,
-        DerivationFunction  kdf,
-        Mac                 mac)
-    {
-        this.agree = agree;
-        this.kdf = kdf;
-        this.mac = mac;
-        this.macBuf = new byte[mac.getMacSize()];
-        this.cipher = null;
-    }
-
-    /**
-     * set up for use in conjunction with a block cipher to handle the
-     * message.
-     *
-     * @param agree the key agreement used as the basis for the encryption
-     * @param kdf the key derivation function used for byte generation
-     * @param mac the message authentication code generator for the message
-     * @param cipher the cipher to used for encrypting the message
-     */
-    public IESEngine(
-        BasicAgreement      agree,
-        DerivationFunction  kdf,
-        Mac                 mac,
-        BufferedBlockCipher cipher)
-    {
-        this.agree = agree;
-        this.kdf = kdf;
-        this.mac = mac;
-        this.macBuf = new byte[mac.getMacSize()];
-        this.cipher = cipher;
-    }
-
-    /**
-     * Initialise the encryptor.
-     *
-     * @param forEncryption whether or not this is encryption/decryption.
-     * @param privParam our private key parameters
-     * @param pubParam the recipient's/sender's public key parameters
-     * @param param encoding and derivation parameters.
-     */
-    public void init(
-        boolean                     forEncryption,
-        CipherParameters            privParam,
-        CipherParameters            pubParam,
-        CipherParameters            param)
-    {
-        this.forEncryption = forEncryption;
-        this.privParam = privParam;
-        this.pubParam = pubParam;
-        this.param = (IESParameters)param;
-    }
-
-    private byte[] decryptBlock(
-        byte[]  in_enc,
-        int     inOff,
-        int     inLen,
-        byte[]  z)
-        throws InvalidCipherTextException
-    {
-        byte[]          M = null;
-        KeyParameter    macKey = null;
-        KDFParameters   kParam = new KDFParameters(z, param.getDerivationV());
-        int             macKeySize = param.getMacKeySize();
-
-        kdf.init(kParam);
-
-        inLen -= mac.getMacSize();
-    
-        if (cipher == null)     // stream mode
-        {
-            byte[]  buf = new byte[inLen + (macKeySize / 8)];
-
-            M = new byte[inLen];
-
-            kdf.generateBytes(buf, 0, buf.length);
-
-            for (int i = 0; i != inLen; i++)
-            {
-                M[i] = (byte)(in_enc[inOff + i] ^ buf[i]);
-            }
-
-            macKey = new KeyParameter(buf, inLen, (macKeySize / 8));
-        }
-        else
-        {
-            int     cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize();
-            byte[]  buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)];
-
-            cipher.init(false, new KeyParameter(buf, 0, (cipherKeySize / 8)));
-
-            byte[] tmp = new byte[cipher.getOutputSize(inLen)];
-
-            int off = cipher.processBytes(in_enc, inOff, inLen, tmp, 0);
-
-            off += cipher.doFinal(tmp, off);
-
-            M = new byte[off];
-
-            System.arraycopy(tmp, 0, M, 0, off);
-
-            macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8));
-        }
-
-        byte[]  macIV = param.getEncodingV();
-
-        mac.init(macKey);
-        mac.update(in_enc, inOff, inLen);
-        mac.update(macIV, 0, macIV.length);
-        mac.doFinal(macBuf, 0);
-    
-        inOff += inLen;
-
-        for (int t = 0; t < macBuf.length; t++)
-        {           
-            if (macBuf[t] != in_enc[inOff + t])
-            {
-                throw (new InvalidCipherTextException("Mac codes failed to equal."));
-            }
-        }
-       
-        return M;
-    }
-
-    private byte[] encryptBlock(
-        byte[]  in,
-        int     inOff,
-        int     inLen,
-        byte[]  z)
-        throws InvalidCipherTextException
-    {
-        byte[]          C = null;
-        KeyParameter    macKey = null;
-        KDFParameters   kParam = new KDFParameters(z, param.getDerivationV());
-        int             c_text_length = 0;
-        int             macKeySize = param.getMacKeySize();
-
-        kdf.init(kParam);
-
-        if (cipher == null)     // stream mode
-        {
-            byte[]  buf = new byte[inLen + (macKeySize / 8)];
-
-            C = new byte[inLen + mac.getMacSize()];
-            c_text_length = inLen;
-
-            kdf.generateBytes(buf, 0, buf.length);
-
-            for (int i = 0; i != inLen; i++)
-            {
-                C[i] = (byte)(in[inOff + i] ^ buf[i]);
-            }
-
-            macKey = new KeyParameter(buf, inLen, (macKeySize / 8));
-        }
-        else
-        {
-            int     cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize();
-            byte[]  buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)];
-
-            cipher.init(true, new KeyParameter(buf, 0, (cipherKeySize / 8)));
-
-            c_text_length = cipher.getOutputSize(inLen);
-
-            C = new byte[c_text_length + mac.getMacSize()];
-
-            int off = cipher.processBytes(in, inOff, inLen, C, 0);
-
-            cipher.doFinal(C, off);
-
-            macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8));
-        }
-
-        byte[]  macIV = param.getEncodingV();
-
-        mac.init(macKey);
-        mac.update(C, 0, c_text_length);
-        mac.update(macIV, 0, macIV.length);
-        //
-        // return the message and it's MAC
-        //
-        mac.doFinal(C, c_text_length);
-        return C;
-    }
-
-    public byte[] processBlock(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
-        throws InvalidCipherTextException
-    {
-        agree.init(privParam);
-
-        BigInteger  z = agree.calculateAgreement(pubParam);
-
-        if (forEncryption)
-        {
-            return encryptBlock(in, inOff, inLen, z.toByteArray());
-        }
-        else
-        {
-            return decryptBlock(in, inOff, inLen, z.toByteArray());
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java b/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
deleted file mode 100644
index 22544ac..0000000
--- a/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.bouncycastle.crypto.engines;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-
-/**
- * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting.
- * Provided for the sake of completeness.
- */
-public class NullEngine implements BlockCipher
-{
-
-    protected static final int BLOCK_SIZE = 1;
-    
-    /**
-     * Standard constructor.
-     */
-    public NullEngine()
-    {
-        super();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.crypto.BlockCipher#init(boolean, org.bouncycastle.crypto.CipherParameters)
-     */
-    public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
-    {
-        // we don't mind any parameters that may come in
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.crypto.BlockCipher#getAlgorithmName()
-     */
-    public String getAlgorithmName()
-    {
-        return "Null";
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.crypto.BlockCipher#getBlockSize()
-     */
-    public int getBlockSize()
-    {
-        return BLOCK_SIZE;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.crypto.BlockCipher#processBlock(byte[], int, byte[], int)
-     */
-    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
-        throws DataLengthException, IllegalStateException
-    {
-            if ((inOff + BLOCK_SIZE) > in.length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + BLOCK_SIZE) > out.length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
-            
-            for (int i = 0; i < BLOCK_SIZE; ++i)
-            {
-                out[outOff + i] = in[inOff + i];
-            }
-            
-            return BLOCK_SIZE;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.crypto.BlockCipher#reset()
-     */
-    public void reset()
-    {
-        // nothing needs to be done
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java b/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java
new file mode 100644
index 0000000..9fb6f55
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/engines/RC532Engine.java
@@ -0,0 +1,287 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995. 
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation has a word size of 32 bits.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC532Engine
+    implements BlockCipher
+{
+    /*
+     * the number of rounds to perform
+     */
+    private int _noRounds;
+
+    /*
+     * the expanded key array of size 2*(rounds + 1)
+     */
+    private int _S[];
+
+    /*
+     * our "magic constants" for 32 32
+     *
+     * Pw = Odd((e-2) * 2^wordsize)
+     * Qw = Odd((o-2) * 2^wordsize)
+     *
+     * where e is the base of natural logarithms (2.718281828...)
+     * and o is the golden ratio (1.61803398...)
+     */
+    private static final int P32 = 0xb7e15163;
+    private static final int Q32 = 0x9e3779b9;
+
+    private boolean forEncryption;
+
+    /**
+     * Create an instance of the RC5 encryption algorithm
+     * and set some defaults
+     */
+    public RC532Engine()
+    {
+        _noRounds     = 12;         // the default
+        _S            = null;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "RC5-32";
+    }
+
+    public int getBlockSize()
+    {
+        return 2 * 4;
+    }
+
+    /**
+     * initialise a RC5-32 cipher.
+     *
+     * @param forEncryption whether or not we are for encryption.
+     * @param params the parameters required to set up the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    params)
+    {
+        if (params instanceof RC5Parameters)
+        {
+            RC5Parameters       p = (RC5Parameters)params;
+
+            _noRounds     = p.getRounds();
+
+            setKey(p.getKey());
+        }
+        else if (params instanceof KeyParameter)
+        {
+            KeyParameter       p = (KeyParameter)params;
+
+            setKey(p.getKey());
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameter passed to RC532 init - " + params.getClass().getName());
+        }
+
+        this.forEncryption = forEncryption;
+    }
+
+    public int processBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        return (forEncryption) ? encryptBlock(in, inOff, out, outOff) 
+                                    : decryptBlock(in, inOff, out, outOff);
+    }
+
+    public void reset()
+    {
+    }
+
+    /**
+     * Re-key the cipher.
+     * <p>
+     * @param  key  the key to be used
+     */
+    private void setKey(
+        byte[]      key)
+    {
+        //
+        // KEY EXPANSION:
+        //
+        // There are 3 phases to the key expansion.
+        //
+        // Phase 1:
+        //   Copy the secret key K[0...b-1] into an array L[0..c-1] of
+        //   c = ceil(b/u), where u = 32/8 in little-endian order.
+        //   In other words, we fill up L using u consecutive key bytes
+        //   of K. Any unfilled byte positions in L are zeroed. In the
+        //   case that b = c = 0, set c = 1 and L[0] = 0.
+        //
+        int[]   L = new int[(key.length + (4 - 1)) / 4];
+
+        for (int i = 0; i != key.length; i++)
+        {
+            L[i / 4] += (key[i] & 0xff) << (8 * (i % 4));
+        }
+
+        //
+        // Phase 2:
+        //   Initialize S to a particular fixed pseudo-random bit pattern
+        //   using an arithmetic progression modulo 2^wordsize determined
+        //   by the magic numbers, Pw & Qw.
+        //
+        _S            = new int[2*(_noRounds + 1)];
+
+        _S[0] = P32;
+        for (int i=1; i < _S.length; i++)
+        {
+            _S[i] = (_S[i-1] + Q32);
+        }
+
+        //
+        // Phase 3:
+        //   Mix in the user's secret key in 3 passes over the arrays S & L.
+        //   The max of the arrays sizes is used as the loop control
+        //
+        int iter;
+
+        if (L.length > _S.length)
+        {
+            iter = 3 * L.length;
+        }
+        else
+        {
+            iter = 3 * _S.length;
+        }
+
+        int A = 0, B = 0;
+        int i = 0, j = 0;
+
+        for (int k = 0; k < iter; k++)
+        {
+            A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+            B =  L[j] = rotateLeft(L[j] + A + B, A+B);
+            i = (i+1) % _S.length;
+            j = (j+1) %  L.length;
+        }
+    }
+
+    /**
+     * Encrypt the given block starting at the given offset and place
+     * the result in the provided buffer starting at the given offset.
+     * <p>
+     * @param  in     in byte buffer containing data to encrypt
+     * @param  inOff  offset into src buffer
+     * @param  out     out buffer where encrypted data is written
+     * @param  outOff  offset into out buffer
+     */
+    private int encryptBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        int A = bytesToWord(in, inOff) + _S[0];
+        int B = bytesToWord(in, inOff + 4) + _S[1];
+
+        for (int i = 1; i <= _noRounds; i++)
+        {
+            A = rotateLeft(A ^ B, B) + _S[2*i];
+            B = rotateLeft(B ^ A, A) + _S[2*i+1];
+        }
+        
+        wordToBytes(A, out, outOff);
+        wordToBytes(B, out, outOff + 4);
+        
+        return 2 * 4;
+    }
+
+    private int decryptBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        int A = bytesToWord(in, inOff);
+        int B = bytesToWord(in, inOff + 4);
+
+        for (int i = _noRounds; i >= 1; i--)
+        {
+            B = rotateRight(B - _S[2*i+1], A) ^ A;
+            A = rotateRight(A - _S[2*i],   B) ^ B;
+        }
+        
+        wordToBytes(A - _S[0], out, outOff);
+        wordToBytes(B - _S[1], out, outOff + 4);
+        
+        return 2 * 4;
+    }
+
+    
+    //////////////////////////////////////////////////////////////
+    //
+    // PRIVATE Helper Methods
+    //
+    //////////////////////////////////////////////////////////////
+
+    /**
+     * Perform a left "spin" of the word. The rotation of the given
+     * word <em>x</em> is rotated left by <em>y</em> bits.
+     * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+     * are used to determine the rotation amount. Here it is 
+     * assumed that the wordsize used is a power of 2.
+     * <p>
+     * @param  x  word to rotate
+     * @param  y    number of bits to rotate % 32
+     */
+    private int rotateLeft(int x, int y)
+    {
+        return ((x << (y & (32-1))) | (x >>> (32 - (y & (32-1)))));
+    }
+
+    /**
+     * Perform a right "spin" of the word. The rotation of the given
+     * word <em>x</em> is rotated left by <em>y</em> bits.
+     * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+     * are used to determine the rotation amount. Here it is 
+     * assumed that the wordsize used is a power of 2.
+     * <p>
+     * @param  x  word to rotate
+     * @param  y    number of bits to rotate % 32
+     */
+    private int rotateRight(int x, int y)
+    {
+        return ((x >>> (y & (32-1))) | (x << (32 - (y & (32-1)))));
+    }
+
+    private int bytesToWord(
+        byte[]  src,
+        int     srcOff)
+    {
+        return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
+            | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
+    }
+
+    private void wordToBytes(
+        int    word,
+        byte[]  dst,
+        int     dstOff)
+    {
+        dst[dstOff] = (byte)word;
+        dst[dstOff + 1] = (byte)(word >> 8);
+        dst[dstOff + 2] = (byte)(word >> 16);
+        dst[dstOff + 3] = (byte)(word >> 24);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java b/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java
new file mode 100644
index 0000000..2121a4b
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/engines/RC564Engine.java
@@ -0,0 +1,288 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995. 
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation is set to work with a 64 bit word size.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC564Engine
+    implements BlockCipher
+{
+    private static final int wordSize = 64;
+    private static final int bytesPerWord = wordSize / 8;
+
+    /*
+     * the number of rounds to perform
+     */
+    private int _noRounds;
+
+    /*
+     * the expanded key array of size 2*(rounds + 1)
+     */
+    private long _S[];
+
+    /*
+     * our "magic constants" for wordSize 62
+     *
+     * Pw = Odd((e-2) * 2^wordsize)
+     * Qw = Odd((o-2) * 2^wordsize)
+     *
+     * where e is the base of natural logarithms (2.718281828...)
+     * and o is the golden ratio (1.61803398...)
+     */
+    private static final long P64 = 0xb7e151628aed2a6bL;
+    private static final long Q64 = 0x9e3779b97f4a7c15L;
+
+    private boolean forEncryption;
+
+    /**
+     * Create an instance of the RC5 encryption algorithm
+     * and set some defaults
+     */
+    public RC564Engine()
+    {
+        _noRounds     = 12;
+        _S            = null;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "RC5-64";
+    }
+
+    public int getBlockSize()
+    {
+        return 2 * bytesPerWord;
+    }
+
+    /**
+     * initialise a RC5-64 cipher.
+     *
+     * @param forEncryption whether or not we are for encryption.
+     * @param params the parameters required to set up the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    params)
+    {
+        if (!(params instanceof RC5Parameters))
+        {
+            throw new IllegalArgumentException("invalid parameter passed to RC564 init - " + params.getClass().getName());
+        }
+
+        RC5Parameters       p = (RC5Parameters)params;
+
+        this.forEncryption = forEncryption;
+
+        _noRounds     = p.getRounds();
+
+        setKey(p.getKey());
+    }
+
+    public int processBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        return (forEncryption) ? encryptBlock(in, inOff, out, outOff) 
+                                    : decryptBlock(in, inOff, out, outOff);
+    }
+
+    public void reset()
+    {
+    }
+
+    /**
+     * Re-key the cipher.
+     * <p>
+     * @param  key  the key to be used
+     */
+    private void setKey(
+        byte[]      key)
+    {
+        //
+        // KEY EXPANSION:
+        //
+        // There are 3 phases to the key expansion.
+        //
+        // Phase 1:
+        //   Copy the secret key K[0...b-1] into an array L[0..c-1] of
+        //   c = ceil(b/u), where u = wordSize/8 in little-endian order.
+        //   In other words, we fill up L using u consecutive key bytes
+        //   of K. Any unfilled byte positions in L are zeroed. In the
+        //   case that b = c = 0, set c = 1 and L[0] = 0.
+        //
+        long[]   L = new long[(key.length + (bytesPerWord - 1)) / bytesPerWord];
+
+        for (int i = 0; i != key.length; i++)
+        {
+            L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord));
+        }
+
+        //
+        // Phase 2:
+        //   Initialize S to a particular fixed pseudo-random bit pattern
+        //   using an arithmetic progression modulo 2^wordsize determined
+        //   by the magic numbers, Pw & Qw.
+        //
+        _S            = new long[2*(_noRounds + 1)];
+
+        _S[0] = P64;
+        for (int i=1; i < _S.length; i++)
+        {
+            _S[i] = (_S[i-1] + Q64);
+        }
+
+        //
+        // Phase 3:
+        //   Mix in the user's secret key in 3 passes over the arrays S & L.
+        //   The max of the arrays sizes is used as the loop control
+        //
+        int iter;
+
+        if (L.length > _S.length)
+        {
+            iter = 3 * L.length;
+        }
+        else
+        {
+            iter = 3 * _S.length;
+        }
+
+        long A = 0, B = 0;
+        int i = 0, j = 0;
+
+        for (int k = 0; k < iter; k++)
+        {
+            A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+            B =  L[j] = rotateLeft(L[j] + A + B, A+B);
+            i = (i+1) % _S.length;
+            j = (j+1) %  L.length;
+        }
+    }
+
+    /**
+     * Encrypt the given block starting at the given offset and place
+     * the result in the provided buffer starting at the given offset.
+     * <p>
+     * @param  in      in byte buffer containing data to encrypt
+     * @param  inOff   offset into src buffer
+     * @param  out     out buffer where encrypted data is written
+     * @param  outOff  offset into out buffer
+     */
+    private int encryptBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        long A = bytesToWord(in, inOff) + _S[0];
+        long B = bytesToWord(in, inOff + bytesPerWord) + _S[1];
+
+        for (int i = 1; i <= _noRounds; i++)
+        {
+            A = rotateLeft(A ^ B, B) + _S[2*i];
+            B = rotateLeft(B ^ A, A) + _S[2*i+1];
+        }
+        
+        wordToBytes(A, out, outOff);
+        wordToBytes(B, out, outOff + bytesPerWord);
+        
+        return 2 * bytesPerWord;
+    }
+
+    private int decryptBlock(
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        long A = bytesToWord(in, inOff);
+        long B = bytesToWord(in, inOff + bytesPerWord);
+
+        for (int i = _noRounds; i >= 1; i--)
+        {
+            B = rotateRight(B - _S[2*i+1], A) ^ A;
+            A = rotateRight(A - _S[2*i],   B) ^ B;
+        }
+        
+        wordToBytes(A - _S[0], out, outOff);
+        wordToBytes(B - _S[1], out, outOff + bytesPerWord);
+        
+        return 2 * bytesPerWord;
+    }
+
+    
+    //////////////////////////////////////////////////////////////
+    //
+    // PRIVATE Helper Methods
+    //
+    //////////////////////////////////////////////////////////////
+
+    /**
+     * Perform a left "spin" of the word. The rotation of the given
+     * word <em>x</em> is rotated left by <em>y</em> bits.
+     * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+     * are used to determine the rotation amount. Here it is 
+     * assumed that the wordsize used is a power of 2.
+     * <p>
+     * @param  x  word to rotate
+     * @param  y    number of bits to rotate % wordSize
+     */
+    private long rotateLeft(long x, long y)
+    {
+        return ((x << (y & (wordSize-1))) | (x >>> (wordSize - (y & (wordSize-1)))));
+    }
+
+    /**
+     * Perform a right "spin" of the word. The rotation of the given
+     * word <em>x</em> is rotated left by <em>y</em> bits.
+     * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+     * are used to determine the rotation amount. Here it is 
+     * assumed that the wordsize used is a power of 2.
+     * <p>
+     * @param  x  word to rotate
+     * @param  y    number of bits to rotate % wordSize
+     */
+    private long rotateRight(long x, long y)
+    {
+        return ((x >>> (y & (wordSize-1))) | (x << (wordSize - (y & (wordSize-1)))));
+    }
+
+    private long bytesToWord(
+        byte[]  src,
+        int     srcOff)
+    {
+        long    word = 0;
+
+        for (int i = bytesPerWord - 1; i >= 0; i--)
+        {
+            word = (word << 8) + (src[i + srcOff] & 0xff);
+        }
+
+        return word;
+    }
+
+    private void wordToBytes(
+        long    word,
+        byte[]  dst,
+        int     dstOff)
+    {
+        for (int i = 0; i < bytesPerWord; i++)
+        {
+            dst[i + dstOff] = (byte)word;
+            word >>>= 8;
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
new file mode 100644
index 0000000..540bd25
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * an implementation of the AES Key Wrapper from the NIST Key Wrap
+ * Specification as described in RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc3394.txt">http://www.ietf.org/rfc/rfc3394.txt</a>
+ * and  <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ */
+public class RFC3394WrapEngine
+    implements Wrapper
+{
+    private BlockCipher     engine;
+    private KeyParameter    param;
+    private boolean         forWrapping;
+
+    private byte[]          iv = {
+                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+
+    public RFC3394WrapEngine(BlockCipher engine)
+    {
+        this.engine = engine;
+    }
+
+    public void init(
+        boolean             forWrapping,
+        CipherParameters    param)
+    {
+        this.forWrapping = forWrapping;
+
+        if (param instanceof ParametersWithRandom)
+        {
+            param = ((ParametersWithRandom) param).getParameters();
+        }
+
+        if (param instanceof KeyParameter)
+        {
+            this.param = (KeyParameter)param;
+        }
+        else if (param instanceof ParametersWithIV)
+        {
+            this.iv = ((ParametersWithIV)param).getIV();
+            this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
+            if (this.iv.length != 8)
+            {
+               throw new IllegalArgumentException("IV not equal to 8");
+            }
+        }
+    }
+
+    public String getAlgorithmName()
+    {
+        return engine.getAlgorithmName();
+    }
+
+    public byte[] wrap(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+    {
+        if (!forWrapping)
+        {
+            throw new IllegalStateException("not set for wrapping");
+        }
+
+        int     n = inLen / 8;
+
+        if ((n * 8) != inLen)
+        {
+            throw new DataLengthException("wrap data must be a multiple of 8 bytes");
+        }
+
+        byte[]  block = new byte[inLen + iv.length];
+        byte[]  buf = new byte[8 + iv.length];
+
+        System.arraycopy(iv, 0, block, 0, iv.length);
+        System.arraycopy(in, 0, block, iv.length, inLen);
+
+        engine.init(true, param);
+
+        for (int j = 0; j != 6; j++)
+        {
+            for (int i = 1; i <= n; i++)
+            {
+                System.arraycopy(block, 0, buf, 0, iv.length);
+                System.arraycopy(block, 8 * i, buf, iv.length, 8);
+                engine.processBlock(buf, 0, buf, 0);
+
+                int t = n * j + i;
+                for (int k = 1; t != 0; k++)
+                {
+                    byte    v = (byte)t;
+
+                    buf[iv.length - k] ^= v;
+
+                    t >>>= 8;
+                }
+
+                System.arraycopy(buf, 0, block, 0, 8);
+                System.arraycopy(buf, 8, block, 8 * i, 8);
+            }
+        }
+
+        return block;
+    }
+
+    public byte[] unwrap(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+        throws InvalidCipherTextException
+    {
+        if (forWrapping)
+        {
+            throw new IllegalStateException("not set for unwrapping");
+        }
+
+        int     n = inLen / 8;
+
+        if ((n * 8) != inLen)
+        {
+            throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+        }
+
+        byte[]  block = new byte[inLen - iv.length];
+        byte[]  a = new byte[iv.length];
+        byte[]  buf = new byte[8 + iv.length];
+
+        System.arraycopy(in, 0, a, 0, iv.length);
+        System.arraycopy(in, iv.length, block, 0, inLen - iv.length);
+
+        engine.init(false, param);
+
+        n = n - 1;
+
+        for (int j = 5; j >= 0; j--)
+        {
+            for (int i = n; i >= 1; i--)
+            {
+                System.arraycopy(a, 0, buf, 0, iv.length);
+                System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+
+                int t = n * j + i;
+                for (int k = 1; t != 0; k++)
+                {
+                    byte    v = (byte)t;
+
+                    buf[iv.length - k] ^= v;
+
+                    t >>>= 8;
+                }
+
+                engine.processBlock(buf, 0, buf, 0);
+                System.arraycopy(buf, 0, a, 0, 8);
+                System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
+            }
+        }
+
+        if (!Arrays.constantTimeAreEqual(a, iv))
+        {
+            throw new InvalidCipherTextException("checksum failed");
+        }
+
+        return block;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
new file mode 100644
index 0000000..e7fb943
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * this does your basic RSA algorithm with blinding
+ */
+public class RSABlindedEngine
+    implements AsymmetricBlockCipher
+{
+    private static BigInteger ONE = BigInteger.valueOf(1);
+
+    private RSACoreEngine    core = new RSACoreEngine();
+    private RSAKeyParameters key;
+    private SecureRandom     random;
+
+    /**
+     * initialise the RSA engine.
+     *
+     * @param forEncryption true if we are encrypting, false otherwise.
+     * @param param the necessary RSA key parameters.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    param)
+    {
+        core.init(forEncryption, param);
+
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+
+            key = (RSAKeyParameters)rParam.getParameters();
+            random = rParam.getRandom();
+        }
+        else
+        {
+            key = (RSAKeyParameters)param;
+            random = new SecureRandom();
+        }
+    }
+
+    /**
+     * Return the maximum size for an input block to this engine.
+     * For RSA this is always one byte less than the key size on
+     * encryption, and the same length as the key size on decryption.
+     *
+     * @return maximum size for an input block.
+     */
+    public int getInputBlockSize()
+    {
+        return core.getInputBlockSize();
+    }
+
+    /**
+     * Return the maximum size for an output block to this engine.
+     * For RSA this is always one byte less than the key size on
+     * decryption, and the same length as the key size on encryption.
+     *
+     * @return maximum size for an output block.
+     */
+    public int getOutputBlockSize()
+    {
+        return core.getOutputBlockSize();
+    }
+
+    /**
+     * Process a single block using the basic RSA algorithm.
+     *
+     * @param in the input array.
+     * @param inOff the offset into the input buffer where the data starts.
+     * @param inLen the length of the data to be processed.
+     * @return the result of the RSA process.
+     * @exception DataLengthException the input block is too large.
+     */
+    public byte[] processBlock(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+    {
+        if (key == null)
+        {
+            throw new IllegalStateException("RSA engine not initialised");
+        }
+
+        BigInteger input = core.convertInput(in, inOff, inLen);
+
+        BigInteger result;
+        if (key instanceof RSAPrivateCrtKeyParameters)
+        {
+            RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+
+            BigInteger e = k.getPublicExponent();
+            if (e != null)   // can't do blinding without a public exponent
+            {
+                BigInteger m = k.getModulus();
+                BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+
+                BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+                BigInteger blindedResult = core.processBlock(blindedInput);
+
+                BigInteger rInv = r.modInverse(m);
+                result = blindedResult.multiply(rInv).mod(m);
+            }
+            else
+            {
+                result = core.processBlock(input);
+            }
+        }
+        else
+        {
+            result = core.processBlock(input);
+        }
+
+        return core.convertOutput(result);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java b/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
similarity index 76%
rename from src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java
rename to src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
index a010b01..510cd5a 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/RSAEngine.java
+++ b/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -1,22 +1,20 @@
 package org.bouncycastle.crypto.engines;
 
-import java.math.BigInteger;
-
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 
+import java.math.BigInteger;
+
 /**
  * this does your basic RSA algorithm.
  */
-public class RSAEngine
-    implements AsymmetricBlockCipher
+class RSACoreEngine
 {
-    private RSAKeyParameters        key;
-    private boolean                 forEncryption;
-    private int                     shift;
+    private RSAKeyParameters key;
+    private boolean          forEncryption;
 
     /**
      * initialise the RSA engine.
@@ -25,22 +23,21 @@
      * @param param the necessary RSA key parameters.
      */
     public void init(
-        boolean             forEncryption,
-        CipherParameters    param)
+        boolean          forEncryption,
+        CipherParameters param)
     {
-        this.key = (RSAKeyParameters)param;
-        this.forEncryption = forEncryption;
-        
-        int     bitSize = key.getModulus().bitLength();
-        
-        if (bitSize % 8 == 0)    // a multiple of 8
+        if (param instanceof ParametersWithRandom)
         {
-            this.shift = 0;
+            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+
+            key = (RSAKeyParameters)rParam.getParameters();
         }
         else
         {
-            this.shift = (8 - (bitSize % 8));
+            key = (RSAKeyParameters)param;
         }
+
+        this.forEncryption = forEncryption;
     }
 
     /**
@@ -85,27 +82,18 @@
         }
     }
 
-    /**
-     * Process a single block using the basic RSA algorithm.
-     *
-     * @param in the input array.
-     * @param inOff the offset into the input buffer where the data starts.
-     * @param inLen the length of the data to be processed.
-     * @return the result of the RSA process.
-     * @exception DataLengthException the input block is too large.
-     */
-    public byte[] processBlock(
+    public BigInteger convertInput(
         byte[]  in,
         int     inOff,
         int     inLen)
     {
         if (inLen > (getInputBlockSize() + 1))
         {
-            throw new DataLengthException("input too large for RSA cipher.\n");
+            throw new DataLengthException("input too large for RSA cipher.");
         }
-        else if (inLen == (getInputBlockSize() + 1) && (in[inOff] & (0x80 >> shift)) != 0)
+        else if (inLen == (getInputBlockSize() + 1) && !forEncryption)
         {
-            throw new DataLengthException("input too large for RSA cipher.\n");
+            throw new DataLengthException("input too large for RSA cipher.");
         }
 
         byte[]  block;
@@ -121,48 +109,19 @@
             block = in;
         }
 
-        BigInteger  input = new BigInteger(1, block);
-        byte[]      output;
-
-        if (key instanceof RSAPrivateCrtKeyParameters)
+        BigInteger res = new BigInteger(1, block);
+        if (res.compareTo(key.getModulus()) >= 0)
         {
-            //
-            // we have the extra factors, use the Chinese Remainder Theorem - the author
-            // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for 
-            // advice regarding the expression of this.
-            //
-            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+            throw new DataLengthException("input too large for RSA cipher.");
+        }
 
-            BigInteger p = crtKey.getP();
-            BigInteger q = crtKey.getQ();
-            BigInteger dP = crtKey.getDP();
-            BigInteger dQ = crtKey.getDQ();
-            BigInteger qInv = crtKey.getQInv();
-    
-            BigInteger mP, mQ, h, m;
-    
-            // mP = ((input mod p) ^ dP)) mod p
-            mP = (input.remainder(p)).modPow(dP, p);
-    
-            // mQ = ((input mod q) ^ dQ)) mod q
-            mQ = (input.remainder(q)).modPow(dQ, q);
-    
-            // h = qInv * (mP - mQ) mod p
-            h = mP.subtract(mQ);
-            h = h.multiply(qInv);
-            h = h.mod(p);               // mod (in Java) returns the positive residual
-    
-            // m = h * q + mQ
-            m = h.multiply(q);
-            m = m.add(mQ);
-    
-            output = m.toByteArray();
-        }
-        else
-        {
-            output = input.modPow(
-                        key.getExponent(), key.getModulus()).toByteArray();
-        }
+        return res;
+    }
+
+    public byte[] convertOutput(
+        BigInteger result)
+    {
+        byte[]      output = result.toByteArray();
 
         if (forEncryption)
         {
@@ -195,6 +154,50 @@
                 return tmp;
             }
         }
+
         return output;
     }
+
+    public BigInteger processBlock(BigInteger input)
+    {
+        if (key instanceof RSAPrivateCrtKeyParameters)
+        {
+            //
+            // we have the extra factors, use the Chinese Remainder Theorem - the author
+            // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
+            // advice regarding the expression of this.
+            //
+            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+
+            BigInteger p = crtKey.getP();
+            BigInteger q = crtKey.getQ();
+            BigInteger dP = crtKey.getDP();
+            BigInteger dQ = crtKey.getDQ();
+            BigInteger qInv = crtKey.getQInv();
+
+            BigInteger mP, mQ, h, m;
+
+            // mP = ((input mod p) ^ dP)) mod p
+            mP = (input.remainder(p)).modPow(dP, p);
+
+            // mQ = ((input mod q) ^ dQ)) mod q
+            mQ = (input.remainder(q)).modPow(dQ, q);
+
+            // h = qInv * (mP - mQ) mod p
+            h = mP.subtract(mQ);
+            h = h.multiply(qInv);
+            h = h.mod(p);               // mod (in Java) returns the positive residual
+
+            // m = h * q + mQ
+            m = h.multiply(q);
+            m = m.add(mQ);
+
+            return m;
+        }
+        else
+        {
+            return input.modPow(
+                        key.getExponent(), key.getModulus());
+        }
+    }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
deleted file mode 100644
index 268ae9b..0000000
--- a/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package org.bouncycastle.crypto.generators;
-
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.params.ISO18033KDFParameters;
-import org.bouncycastle.crypto.params.KDFParameters;
-
-/**
- * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
- * <br>
- * This implementation is based on ISO 18033/P1363a.
- */
-public class BaseKDFBytesGenerator
-    implements DerivationFunction
-{
-    private int     counterStart;
-    private Digest  digest;
-    private byte[]  shared;
-    private byte[]  iv;
-
-    /**
-     * Construct a KDF Parameters generator.
-     * <p>
-     * @param counterStart value of counter.
-     * @param digest the digest to be used as the source of derived keys.
-     */
-    protected BaseKDFBytesGenerator(
-        int     counterStart,
-        Digest  digest)
-    {
-        this.counterStart = counterStart;
-        this.digest = digest;
-    }
-
-    public void init(
-        DerivationParameters    param)
-    {
-        if (param instanceof KDFParameters)
-        {
-            KDFParameters   p = (KDFParameters)param;
-
-            shared = p.getSharedSecret();
-            iv = p.getIV();
-        }
-        else if (param instanceof ISO18033KDFParameters)
-        {
-            ISO18033KDFParameters p = (ISO18033KDFParameters)param;
-            
-            shared = p.getSeed();
-            iv = null;
-        }
-        else
-        {
-            throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
-        }
-    }
-
-    /**
-     * return the underlying digest.
-     */
-    public Digest getDigest()
-    {
-        return digest;
-    }
-
-    /**
-     * fill len bytes of the output buffer with bytes generated from
-     * the derivation function.
-     *
-     * @throws IllegalArgumentException if the size of the request will cause an overflow.
-     * @throws DataLengthException if the out buffer is too small.
-     */
-    public int generateBytes(
-        byte[]  out,
-        int     outOff,
-        int     len)
-        throws DataLengthException, IllegalArgumentException
-    {
-        if ((out.length - len) < outOff)
-        {
-            throw new DataLengthException("output buffer too small");
-        }
-
-        long    oBytes = len;
-        int     outLen = digest.getDigestSize(); 
-
-        //
-        // this is at odds with the standard implementation, the
-        // maximum value should be hBits * (2^32 - 1) where hBits
-        // is the digest output size in bits. We can't have an
-        // array with a long index at the moment...
-        //
-        if (oBytes > ((2L << 32) - 1))
-        {
-            throw new IllegalArgumentException("Output length too large");
-        }
-
-        int cThreshold = (int)((oBytes + outLen - 1) / outLen);
-
-        byte[] dig = null;
-
-        dig = new byte[digest.getDigestSize()];
-
-        int counter = counterStart;
-        
-        for (int i = 0; i < cThreshold; i++)
-        {
-            digest.update(shared, 0, shared.length);
-
-            digest.update((byte)(counter >> 24));
-            digest.update((byte)(counter >> 16));
-            digest.update((byte)(counter >> 8));
-            digest.update((byte)counter);
-            
-            if (iv != null)
-            {
-                digest.update(iv, 0, iv.length);
-            }
-
-            digest.doFinal(dig, 0);
-
-            if (len > outLen)
-            {
-                System.arraycopy(dig, 0, out, outOff, outLen);
-                outOff += outLen;
-                len -= outLen;
-            }
-            else
-            {
-                System.arraycopy(dig, 0, out, outOff, len);
-            }
-            
-            counter++;
-        }
-    
-        digest.reset();
-
-        return len;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
index bb3f6d0..7111118 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
@@ -1,11 +1,36 @@
 package org.bouncycastle.crypto.generators;
 
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.params.DESParameters;
 
 public class DESKeyGenerator
     extends CipherKeyGenerator
 {
+    /**
+     * initialise the key generator - if strength is set to zero
+     * the key generated will be 64 bits in size, otherwise
+     * strength can be 64 or 56 bits (if you don't count the parity bits).
+     *
+     * @param param the parameters to be used for key generation
+     */
+    public void init(
+        KeyGenerationParameters param)
+    {
+        super.init(param);
+
+        if (strength == 0 || strength == (56 / 8))
+        {
+            strength = DESParameters.DES_KEY_LENGTH;
+        }
+        else if (strength != DESParameters.DES_KEY_LENGTH)
+        {
+            throw new IllegalArgumentException("DES key must be "
+                    + (DESParameters.DES_KEY_LENGTH * 8)
+                    + " bits long.");
+        }
+    }
+
     public byte[] generateKey()
     {
         byte[]  newKey = new byte[DESParameters.DES_KEY_LENGTH];
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
index 101dc37..3cab983 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
@@ -18,7 +18,8 @@
     public void init(
         KeyGenerationParameters param)
     {
-        super.init(param);
+        this.random = param.getRandom();
+        this.strength = (param.getStrength() + 7) / 8;
 
         if (strength == 0 || strength == (168 / 8))
         {
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
index 213c052..f93428e 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.crypto.generators;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
@@ -10,16 +8,17 @@
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 
+import java.math.BigInteger;
+
 /**
- * a basic Diffie-Helman key pair generator.
+ * a basic Diffie-Hellman key pair generator.
  *
  * This generates keys consistent for use with the basic algorithm for
- * Diffie-Helman.
+ * Diffie-Hellman.
  */
 public class DHBasicKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator
 {
-    private DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
     private DHKeyGenerationParameters param;
 
     public void init(
@@ -30,15 +29,14 @@
 
     public AsymmetricCipherKeyPair generateKeyPair()
     {
-        BigInteger      p, x, y;
-        DHParameters    dhParams = param.getParameters();
+        DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
+        DHParameters dhp = param.getParameters();
 
-        p = dhParams.getP();
-        x = helper.calculatePrivate(p, param.getRandom(), dhParams.getJ()); 
-        y = helper.calculatePublic(p, dhParams.getG(), x);
+        BigInteger x = helper.calculatePrivate(dhp, param.getRandom()); 
+        BigInteger y = helper.calculatePublic(dhp, x);
 
         return new AsymmetricCipherKeyPair(
-                new DHPublicKeyParameters(y, dhParams),
-                new DHPrivateKeyParameters(x, dhParams));
+            new DHPublicKeyParameters(y, dhp),
+            new DHPrivateKeyParameters(x, dhp));
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java b/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
index abbda43..e0d86fc 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
@@ -3,66 +3,49 @@
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.util.BigIntegers;
+
 class DHKeyGeneratorHelper
 {
-    private static final int MAX_ITERATIONS = 1000;
-
     static final DHKeyGeneratorHelper INSTANCE = new DHKeyGeneratorHelper();
-    
-    private static BigInteger ZERO = BigInteger.valueOf(0);
-    private static BigInteger TWO = BigInteger.valueOf(2);
-    
+
+    private static final BigInteger ONE = BigInteger.valueOf(1);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
     private DHKeyGeneratorHelper()
     {
     }
-    
-    BigInteger calculatePrivate(BigInteger p, SecureRandom random, int limit)
+
+    BigInteger calculatePrivate(DHParameters dhParams, SecureRandom random)
     {
-        //
-        // calculate the private key
-        //
-        BigInteger pSub2 = p.subtract(TWO);
-        BigInteger x;
-        
-        if (limit == 0)
+        BigInteger p = dhParams.getP();
+        int limit = dhParams.getL();
+
+        if (limit != 0)
         {
-            x = createInRange(pSub2, random);
+            return new BigInteger(limit, random).setBit(limit - 1);
         }
-        else
+
+        BigInteger min = TWO;
+        int m = dhParams.getM();
+        if (m != 0)
         {
-            do
-            {
-                x = new BigInteger(limit, random);
-            }
-            while (x.equals(ZERO));
+            min = ONE.shiftLeft(m - 1);
         }
-        
-        return x;
+
+        BigInteger max = p.subtract(TWO);
+        BigInteger q = dhParams.getQ();
+        if (q != null)
+        {
+            max = q.subtract(TWO);
+        }
+
+        return BigIntegers.createRandomInRange(min, max, random);
     }
 
-    private BigInteger createInRange(BigInteger max, SecureRandom random)
+    BigInteger calculatePublic(DHParameters dhParams, BigInteger x)
     {
-        BigInteger x;
-        int maxLength = max.bitLength();
-        int count = 0;
-        
-        do
-        {
-            x = new BigInteger(maxLength, random);
-            count++;
-        }
-        while ((x.equals(ZERO) || x.compareTo(max) > 0) && count != MAX_ITERATIONS);
-        
-        if (count == MAX_ITERATIONS)  // fall back to a faster (restricted) method
-        {
-            return new BigInteger(maxLength - 1, random).setBit(0);
-        }
-        
-        return x;
-    }
-    
-    BigInteger calculatePublic(BigInteger p, BigInteger g, BigInteger x)
-    {
-        return g.modPow(x, p);
+        return dhParams.getG().modPow(x, dhParams.getP());
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java
deleted file mode 100644
index 8c734cd..0000000
--- a/src/main/java/org/bouncycastle/crypto/generators/DHKeyPairGenerator.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.bouncycastle.crypto.generators;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
-import org.bouncycastle.crypto.KeyGenerationParameters;
-import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
-import org.bouncycastle.crypto.params.DHParameters;
-import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
-import org.bouncycastle.crypto.params.DHPublicKeyParameters;
-
-/**
- * a Diffie-Helman key pair generator.
- *
- * This generates keys consistent for use in the MTI/A0 key agreement protocol
- * as described in "Handbook of Applied Cryptography", Pages 516-519.
- */
-public class DHKeyPairGenerator
-    implements AsymmetricCipherKeyPairGenerator
-{
-    private DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.INSTANCE;
-    
-    private DHKeyGenerationParameters param;
-
-    public void init(
-        KeyGenerationParameters param)
-    {
-        this.param = (DHKeyGenerationParameters)param;
-    }
-
-    public AsymmetricCipherKeyPair generateKeyPair()
-    {
-        BigInteger      p, x, y;
-        DHParameters    dhParams = param.getParameters();
-        
-        p = dhParams.getP();
-        x = helper.calculatePrivate(p, param.getRandom(), dhParams.getJ()); 
-        y = helper.calculatePublic(p, dhParams.getG(), x);
-
-        return new AsymmetricCipherKeyPair(
-                new DHPublicKeyParameters(y, dhParams),
-                new DHPrivateKeyParameters(x, dhParams));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
index f3dff34..f5d4264 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
@@ -1,18 +1,17 @@
 package org.bouncycastle.crypto.generators;
 
+import org.bouncycastle.crypto.params.DHParameters;
+
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
-import org.bouncycastle.crypto.params.DHParameters;
-
 public class DHParametersGenerator
 {
     private int             size;
     private int             certainty;
     private SecureRandom    random;
 
-    private static BigInteger ONE = BigInteger.valueOf(1);
-    private static BigInteger TWO = BigInteger.valueOf(2);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
 
     /**
      * Initialise the parameters generator.
@@ -39,43 +38,15 @@
      */
     public DHParameters generateParameters()
     {
-        BigInteger      g, p, q;
-        int             qLength = size - 1;
-
         //
         // find a safe prime p where p = 2*q + 1, where p and q are prime.
         //
-        for (;;)
-        {
-            q = new BigInteger(qLength, certainty, random);
-            p = q.multiply(TWO).add(ONE);
-            if (p.isProbablePrime(certainty))
-            {
-                break;
-            }
-        }
+        BigInteger[] safePrimes = DHParametersHelper.generateSafePrimes(size, certainty, random);
 
-        //
-        // calculate the generator g - the advantage of using the 2q+1 
-        // approach is that we know the prime factorisation of (p - 1)...
-        //
-        for (;;)
-        {
-            g = new BigInteger(qLength, random);
+        BigInteger p = safePrimes[0];
+        BigInteger q = safePrimes[1];
+        BigInteger g = DHParametersHelper.selectGenerator(p, q, random);
 
-            if (g.modPow(TWO, p).equals(ONE))
-            {
-                continue;
-            }
-
-            if (g.modPow(q, p).equals(ONE))
-            {
-                continue;
-            }
-
-            break;
-        }
-
-        return new DHParameters(p, g, q, 2);
+        return new DHParameters(p, g, q, TWO, null);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java b/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
new file mode 100644
index 0000000..2554c30
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.util.BigIntegers;
+
+class DHParametersHelper
+{
+    private static final BigInteger ONE = BigInteger.valueOf(1);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
+    // Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
+    static BigInteger[] generateSafePrimes(
+        int             size,
+        int             certainty,
+        SecureRandom    random)
+    {
+        BigInteger p, q;
+        int qLength = size - 1;
+
+        for (;;)
+        {
+            q = new BigInteger(qLength, 2, random);
+
+            // p <- 2q + 1
+            p = q.shiftLeft(1).add(ONE);
+
+            if (p.isProbablePrime(certainty)
+                && (certainty <= 2 || q.isProbablePrime(certainty)))
+            {
+                    break;
+            }
+        }
+
+        return new BigInteger[] { p, q };
+    }
+
+    // Select a high order element of the multiplicative group Zp*
+    // p and q must be s.t. p = 2*q + 1, where p and q are prime
+    static BigInteger selectGenerator(
+        BigInteger      p,
+        BigInteger      q,
+        SecureRandom    random)
+    {
+        BigInteger pMinusTwo = p.subtract(TWO);
+        BigInteger g;
+
+        // Handbook of Applied Cryptography 4.86
+        do
+        {
+            g = BigIntegers.createRandomInRange(TWO, pMinusTwo, random);
+        }
+        while (g.modPow(TWO, p).equals(ONE)
+            || g.modPow(q, p).equals(ONE));
+
+/*
+        // RFC 2631 2.1.1 (and see Handbook of Applied Cryptography 4.81)
+        do
+        {
+            BigInteger h = createInRange(TWO, pMinusTwo, random);
+
+            g = h.modPow(TWO, p);
+        }
+        while (g.equals(ONE));
+*/
+
+        return g;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
index e1a9655..93f49cf 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.crypto.generators;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
@@ -10,17 +7,21 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
 
 /**
  * a DSA key pair generator.
  *
  * This generates DSA keys in line with the method described 
- * in FIPS 186-2.
+ * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>.
  */
 public class DSAKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator
 {
-    private static BigInteger ZERO = BigInteger.valueOf(0);
+    private static final BigInteger ONE = BigInteger.valueOf(1);
 
     private DSAKeyGenerationParameters param;
 
@@ -32,27 +33,29 @@
 
     public AsymmetricCipherKeyPair generateKeyPair()
     {
-        BigInteger      p, q, g, x, y;
-        DSAParameters   dsaParams = param.getParameters();
-        SecureRandom    random = param.getRandom();
+        DSAParameters dsaParams = param.getParameters();
 
-        q = dsaParams.getQ();
-        p = dsaParams.getP();
-        g = dsaParams.getG();
-
-        do
-        {
-            x = new BigInteger(160, random);
-        }
-        while (x.equals(ZERO)  || x.compareTo(q) >= 0);
-
-        //
-        // calculate the public key.
-        //
-        y = g.modPow(x, p);
+        BigInteger x = generatePrivateKey(dsaParams.getQ(), param.getRandom());
+        BigInteger y = calculatePublicKey(dsaParams.getP(), dsaParams.getG(), x);
 
         return new AsymmetricCipherKeyPair(
-                new DSAPublicKeyParameters(y, dsaParams),
-                new DSAPrivateKeyParameters(x, dsaParams));
+            new DSAPublicKeyParameters(y, dsaParams),
+            new DSAPrivateKeyParameters(x, dsaParams));
+    }
+
+    private static BigInteger generatePrivateKey(BigInteger q, SecureRandom random)
+    {
+        // TODO Prefer this method? (change test cases that used fixed random)
+        // B.1.1 Key Pair Generation Using Extra Random Bits
+//        BigInteger c = new BigInteger(q.bitLength() + 64, random);
+//        return c.mod(q.subtract(ONE)).add(ONE);
+
+        // B.1.2 Key Pair Generation by Testing Candidates
+        return BigIntegers.createRandomInRange(ONE, q.subtract(ONE), random);
+    }
+
+    private static BigInteger calculatePublicKey(BigInteger p, BigInteger g, BigInteger x)
+    {
+        return g.modPow(x, p);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
index 4996b1e..aa8a4cc 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -1,23 +1,29 @@
 package org.bouncycastle.crypto.generators;
 
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
 import java.math.BigInteger;
 import java.security.SecureRandom;
 
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.params.DSAParameters;
-import org.bouncycastle.crypto.params.DSAValidationParameters;
-
+// TODO Update javadoc to mention FIPS 186-3 when done
 /**
  * generate suitable parameters for DSA, in line with FIPS 186-2.
  */
 public class DSAParametersGenerator
 {
-    private int             size;
+    private int             L, N;
     private int             certainty;
     private SecureRandom    random;
 
-    private static BigInteger ONE = BigInteger.valueOf(1);
-    private static BigInteger TWO = BigInteger.valueOf(2);
+    private static final BigInteger ZERO = BigInteger.valueOf(0);
+    private static final BigInteger ONE = BigInteger.valueOf(1);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
 
     /**
      * initialise the key generator.
@@ -31,32 +37,23 @@
         int             certainty,
         SecureRandom    random)
     {
-        this.size = size;
-        this.certainty = certainty;
-        this.random = random;
+        init(size, getDefaultN(size), certainty, random);
     }
 
-    /**
-     * add value to b, returning the result in a. The a value is treated
-     * as a BigInteger of length (a.length * 8) bits. The result is
-     * modulo 2^a.length in case of overflow.
-     */
-    private void add(
-        byte[]  a,
-        byte[]  b,
-        int     value)
+    // TODO Make public to enable support for DSA keys > 1024 bits
+    private void init(
+        int             L,
+        int             N,
+        int             certainty,
+        SecureRandom    random)
     {
-        int     x = (b[b.length - 1] & 0xff) + value;
+        // TODO Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2)
+        // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1?
 
-        a[b.length - 1] = (byte)x;
-        x >>>= 8;
-
-        for (int i = b.length - 2; i >= 0; i--)
-        {
-            x += (b[i] & 0xff);
-            a[i] = (byte)x;
-            x >>>= 8;
-        }
+        this.L = L;
+        this.N = N;
+        this.certainty = certainty;
+        this.random = random;
     }
 
     /**
@@ -67,112 +64,274 @@
      */
     public DSAParameters generateParameters()
     {
+        return L > 1024
+            ? generateParameters_FIPS186_3()
+            : generateParameters_FIPS186_2();
+    }
+
+    private DSAParameters generateParameters_FIPS186_2()
+    {
         byte[]          seed = new byte[20];
         byte[]          part1 = new byte[20];
         byte[]          part2 = new byte[20];
         byte[]          u = new byte[20];
         SHA1Digest      sha1 = new SHA1Digest();
-        int             n = (size - 1) / 160;
-        byte[]          w = new byte[size / 8];
+        int             n = (L - 1) / 160;
+        byte[]          w = new byte[L / 8];
 
-        BigInteger      q = null, p = null, g = null;
-        int             counter = 0;
-        boolean         primesFound = false;
-
-        while (!primesFound)
+        for (;;)
         {
-            do
+            random.nextBytes(seed);
+
+            hash(sha1, seed, part1);
+            System.arraycopy(seed, 0, part2, 0, seed.length);
+            inc(part2);
+            hash(sha1, part2, part2);
+
+            for (int i = 0; i != u.length; i++)
             {
-                random.nextBytes(seed);
-
-                sha1.update(seed, 0, seed.length);
-
-                sha1.doFinal(part1, 0);
-
-                System.arraycopy(seed, 0, part2, 0, seed.length);
-
-                add(part2, seed, 1);
-
-                sha1.update(part2, 0, part2.length);
-
-                sha1.doFinal(part2, 0);
-
-                for (int i = 0; i != u.length; i++)
-                {
-                    u[i] = (byte)(part1[i] ^ part2[i]);
-                }
-
-                u[0] |= (byte)0x80;
-                u[19] |= (byte)0x01;
-
-                q = new BigInteger(1, u);
+                u[i] = (byte)(part1[i] ^ part2[i]);
             }
-            while (!q.isProbablePrime(certainty));
 
-            counter = 0;
+            u[0] |= (byte)0x80;
+            u[19] |= (byte)0x01;
 
-            int offset = 2;
+            BigInteger q = new BigInteger(1, u);
 
-            while (counter < 4096)
+            if (!q.isProbablePrime(certainty))
+            {
+                continue;
+            }
+
+            byte[] offset = Arrays.clone(seed);
+            inc(offset);
+
+            for (int counter = 0; counter < 4096; ++counter)
             {
                 for (int k = 0; k < n; k++)
                 {
-                    add(part1, seed, offset + k);
-                    sha1.update(part1, 0, part1.length);
-                    sha1.doFinal(part1, 0);
+                    inc(offset);
+                    hash(sha1, offset, part1);
                     System.arraycopy(part1, 0, w, w.length - (k + 1) * part1.length, part1.length);
                 }
 
-                add(part1, seed, offset + n);
-                sha1.update(part1, 0, part1.length);
-                sha1.doFinal(part1, 0);
+                inc(offset);
+                hash(sha1, offset, part1);
                 System.arraycopy(part1, part1.length - ((w.length - (n) * part1.length)), w, 0, w.length - n * part1.length);
 
                 w[0] |= (byte)0x80;
 
-                BigInteger  x = new BigInteger(1, w);
+                BigInteger x = new BigInteger(1, w);
 
-                BigInteger  c = x.mod(q.multiply(TWO));
+                BigInteger c = x.mod(q.shiftLeft(1));
 
-                p = x.subtract(c.subtract(ONE));
+                BigInteger p = x.subtract(c.subtract(ONE));
 
-                if (p.testBit(size - 1))
+                if (p.bitLength() != L)
                 {
-                    if (p.isProbablePrime(certainty))
-                    {
-                        primesFound = true;
-                        break;
-                    }
+                    continue;
                 }
 
-                counter += 1;
-                offset += n + 1;
+                if (p.isProbablePrime(certainty))
+                {
+                    BigInteger g = calculateGenerator_FIPS186_2(p, q, random);
+
+                    return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter));
+                }
             }
         }
+    }
 
-        //
-        // calculate the generator g
-        //
-        BigInteger  pMinusOneOverQ = p.subtract(ONE).divide(q);
+    private static BigInteger calculateGenerator_FIPS186_2(BigInteger p, BigInteger q, SecureRandom r)
+    {
+        BigInteger e = p.subtract(ONE).divide(q);
+        BigInteger pSub2 = p.subtract(TWO);
 
         for (;;)
         {
-            BigInteger h = new BigInteger(size, random);
-            
-            if (h.compareTo(ONE) <= 0 || h.compareTo(p.subtract(ONE)) >= 0)
+            BigInteger h = BigIntegers.createRandomInRange(TWO, pSub2, r);
+            BigInteger g = h.modPow(e, p);
+            if (g.bitLength() > 1)
             {
-                continue;
+                return g;
             }
-
-            g = h.modPow(pMinusOneOverQ, p);
-            if (g.compareTo(ONE) <= 0)
-            {
-                continue;
-            }
-
-            break;
         }
+    }
 
-        return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter));
+    /**
+     * generate suitable parameters for DSA, in line with
+     * <i>FIPS 186-3 A.1 Generation of the FFC Primes p and q</i>.
+     */
+    private DSAParameters generateParameters_FIPS186_3()
+    {
+// A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function
+        // FIXME This should be configurable (digest size in bits must be >= N)
+        Digest d = new SHA256Digest();
+        int outlen = d.getDigestSize() * 8;
+
+// 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If
+//    the pair is not in the list, then return INVALID.
+        // Note: checked at initialisation
+
+// 2. If (seedlen < N), then return INVALID.
+        // FIXME This should be configurable (must be >= N)
+        int seedlen = N;
+        byte[] seed = new byte[seedlen / 8];
+
+// 3. n = ⎡L ⁄ outlen⎤ – 1.
+        int n = (L - 1) / outlen;
+
+// 4. b = L – 1 – (n ∗ outlen).
+        int b = (L - 1) % outlen;
+
+        byte[] output = new byte[d.getDigestSize()];
+        for (;;)
+        {
+// 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed.
+            random.nextBytes(seed);
+
+// 6. U = Hash (domain_parameter_seed) mod 2^(N–1).
+            hash(d, seed, output);
+            BigInteger U = new BigInteger(1, output).mod(ONE.shiftLeft(N - 1));
+
+// 7. q = 2^(N–1) + U + 1 – ( U mod 2).
+            BigInteger q = ONE.shiftLeft(N - 1).add(U).add(ONE).subtract(U.mod(TWO));
+
+// 8. Test whether or not q is prime as specified in Appendix C.3.
+            // TODO Review C.3 for primality checking
+            if (!q.isProbablePrime(certainty))
+            {
+// 9. If q is not a prime, then go to step 5.
+                continue;
+            }
+
+// 10. offset = 1.
+            // Note: 'offset' value managed incrementally
+            byte[] offset = Arrays.clone(seed);
+
+// 11. For counter = 0 to (4L – 1) do
+            int counterLimit = 4 * L;
+            for (int counter = 0; counter < counterLimit; ++counter)
+            {
+// 11.1 For j = 0 to n do
+//      Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen).
+// 11.2 W = V0 + (V1 ∗ 2^outlen) + ... + (V^(n–1) ∗ 2^((n–1) ∗ outlen)) + ((Vn mod 2^b) ∗ 2^(n ∗ outlen)).
+                // TODO Assemble w as a byte array
+                BigInteger W = ZERO;
+                for (int j = 0, exp = 0; j <= n; ++j, exp += outlen)
+                {
+                    inc(offset);
+                    hash(d, offset, output);
+
+                    BigInteger Vj = new BigInteger(1, output);
+                    if (j == n)
+                    {
+                        Vj = Vj.mod(ONE.shiftLeft(b));
+                    }
+
+                    W = W.add(Vj.shiftLeft(exp));
+                }
+
+// 11.3 X = W + 2^(L–1). Comment: 0 ≤ W < 2L–1; hence, 2L–1 ≤ X < 2L.
+                BigInteger X = W.add(ONE.shiftLeft(L - 1));
+ 
+// 11.4 c = X mod 2q.
+                BigInteger c = X.mod(q.shiftLeft(1));
+
+// 11.5 p = X - (c - 1). Comment: p ≡ 1 (mod 2q).
+                BigInteger p = X.subtract(c.subtract(ONE));
+
+// 11.6 If (p < 2^(L - 1)), then go to step 11.9
+                if (p.bitLength() != L)
+                {
+                    continue;
+                }
+
+// 11.7 Test whether or not p is prime as specified in Appendix C.3.
+                // TODO Review C.3 for primality checking
+                if (p.isProbablePrime(certainty))
+                {
+// 11.8 If p is determined to be prime, then return VALID and the values of p, q and
+//      (optionally) the values of domain_parameter_seed and counter.
+                    // TODO Make configurable (8-bit unsigned)?
+//                    int index = 1;
+//                    BigInteger g = calculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, index);
+//                    if (g != null)
+//                    {
+//                        // TODO Should 'index' be a part of the validation parameters?
+//                        return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter));
+//                    }
+
+                    BigInteger g = calculateGenerator_FIPS186_3_Unverifiable(p, q, random);
+                    return new DSAParameters(p, q, g, new DSAValidationParameters(seed, counter));
+                }
+
+// 11.9 offset = offset + n + 1.      Comment: Increment offset; then, as part of
+//                                    the loop in step 11, increment counter; if
+//                                    counter < 4L, repeat steps 11.1 through 11.8.
+                // Note: 'offset' value already incremented in inner loop
+            }
+// 12. Go to step 5.
+        }
+    }
+
+    private static BigInteger calculateGenerator_FIPS186_3_Unverifiable(BigInteger p, BigInteger q,
+        SecureRandom r)
+    {
+        return calculateGenerator_FIPS186_2(p, q, r);
+    }
+
+//    private static BigInteger calculateGenerator_FIPS186_3_Verifiable(Digest d, BigInteger p, BigInteger q,
+//        byte[] seed, int index)
+//    {
+//// A.2.3 Verifiable Canonical Generation of the Generator g
+//        BigInteger e = p.subtract(ONE).divide(q);
+//        byte[] ggen = Hex.decode("6767656E");
+//
+//        // 7. U = domain_parameter_seed || “ggen” || index || count.
+//        byte[] U = new byte[seed.length + ggen.length + 1 + 2];
+//        System.arraycopy(seed, 0, U, 0, seed.length);
+//        System.arraycopy(ggen, 0, U, seed.length, ggen.length);
+//        U[U.length - 3] = (byte)index; 
+//
+//        byte[] w = new byte[d.getDigestSize()];
+//        for (int count = 1; count < (1 << 16); ++count)
+//        {
+//            inc(U);
+//            hash(d, U, w);
+//            BigInteger W = new BigInteger(1, w);
+//            BigInteger g = W.modPow(e, p);
+//            if (g.compareTo(TWO) >= 0)
+//            {
+//                return g;
+//            }
+//        }
+//
+//        return null;
+//    }
+
+    private static void hash(Digest d, byte[] input, byte[] output)
+    {
+        d.update(input, 0, input.length);
+        d.doFinal(output, 0);
+    }
+
+    private static int getDefaultN(int L)
+    {
+        return L > 1024 ? 256 : 160;
+    }
+
+    private static void inc(byte[] buf)
+    {
+        for (int i = buf.length - 1; i >= 0; --i)
+        {
+            byte b = (byte)((buf[i] + 1) & 0xff);
+            buf[i] = b;
+
+            if (b != 0)
+            {
+                break;
+            }
+        }
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java
deleted file mode 100644
index 7789b7b..0000000
--- a/src/main/java/org/bouncycastle/crypto/generators/KDF1BytesGenerator.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.bouncycastle.crypto.generators;
-
-import org.bouncycastle.crypto.Digest;
-
-/**
- * KDF1 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
- * <br>
- * This implementation is based on ISO 18033/IEEE P1363a.
- */
-public class KDF1BytesGenerator
-    extends BaseKDFBytesGenerator
-{
-    /**
-     * Construct a KDF1 byte generator.
-     * <p>
-     * @param digest the digest to be used as the source of derived keys.
-     */
-    public KDF1BytesGenerator(
-        Digest  digest)
-    {
-        super(0, digest);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java
deleted file mode 100644
index cab05ac..0000000
--- a/src/main/java/org/bouncycastle/crypto/generators/KDF2BytesGenerator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.bouncycastle.crypto.generators;
-
-import org.bouncycastle.crypto.Digest;
-
-/**
- * KFD2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
- * <br>
- * This implementation is based on IEEE P1363/ISO 18033.
- */
-public class KDF2BytesGenerator
-    extends BaseKDFBytesGenerator
-{
-    /**
-     * Construct a KDF2 bytes generator. Generates key material
-     * according to IEEE P1363 or ISO 18033 depending on the initialisation.
-     * <p>
-     * @param digest the digest to be used as the source of derived keys.
-     */
-    public KDF2BytesGenerator(
-        Digest  digest)
-    {
-        super(1, digest);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
deleted file mode 100644
index e93c0d7..0000000
--- a/src/main/java/org/bouncycastle/crypto/generators/MGF1BytesGenerator.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.bouncycastle.crypto.generators;
-
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.params.MGFParameters;
-
-/**
- * Generator for MGF1 as defined in PKCS 1v2
- */
-public class MGF1BytesGenerator
-    implements DerivationFunction
-{
-    private Digest  digest;
-    private byte[]  seed;
-    private int     hLen;
-
-    /**
-     * @param digest the digest to be used as the source of generated bytes
-     */
-    public MGF1BytesGenerator(
-        Digest  digest)
-    {
-        this.digest = digest;
-        this.hLen = digest.getDigestSize();
-    }
-
-    public void init(
-        DerivationParameters    param)
-    {
-        if (!(param instanceof MGFParameters))
-        {
-            throw new IllegalArgumentException("MGF parameters required for MGF1Generator");
-        }
-
-        MGFParameters   p = (MGFParameters)param;
-
-        seed = p.getSeed();
-    }
-
-    /**
-     * return the underlying digest.
-     */
-    public Digest getDigest()
-    {
-        return digest;
-    }
-
-    /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
-     * fill len bytes of the output buffer with bytes generated from
-     * the derivation function.
-     *
-     * @throws DataLengthException if the out buffer is too small.
-     */
-    public int generateBytes(
-        byte[]  out,
-        int     outOff,
-        int     len)
-        throws DataLengthException, IllegalArgumentException
-    {
-        if ((out.length - len) < outOff)
-        {
-            throw new DataLengthException("output buffer too small");
-        }
-        
-        byte[]  hashBuf = new byte[hLen];
-        byte[]  C = new byte[4];
-        int     counter = 0;
-
-        digest.reset();
-
-        if (len > hLen)
-        {
-            do
-            {
-                ItoOSP(counter, C);
-    
-                digest.update(seed, 0, seed.length);
-                digest.update(C, 0, C.length);
-                digest.doFinal(hashBuf, 0);
-    
-                System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, hLen);
-            }
-            while (++counter < (len / hLen));
-        }
-
-        if ((counter * hLen) < len)
-        {
-            ItoOSP(counter, C);
-
-            digest.update(seed, 0, seed.length);
-            digest.update(C, 0, C.length);
-            digest.doFinal(hashBuf, 0);
-
-            System.arraycopy(hashBuf, 0, out, outOff + counter * hLen, len - (counter * hLen));
-        }
-
-        return len;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index b881104..f58069d 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.crypto.generators;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
@@ -9,13 +7,15 @@
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 
+import java.math.BigInteger;
+
 /**
  * an RSA key pair generator.
  */
 public class RSAKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator
 {
-    private static BigInteger ONE = BigInteger.valueOf(1);
+    private static final BigInteger ONE = BigInteger.valueOf(1);
 
     private RSAKeyGenerationParameters param;
 
@@ -32,11 +32,16 @@
         //
         // p and q values should have a length of half the strength in bits
         //
-        int pbitlength = (param.getStrength() + 1) / 2;
-        int qbitlength = (param.getStrength() - pbitlength);
+        int strength = param.getStrength();
+        int pbitlength = (strength + 1) / 2;
+        int qbitlength = strength - pbitlength;
+        int mindiffbits = strength / 3;
 
         e = param.getPublicExponent();
 
+        // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+        // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+
         //
         // generate p, prime and (p-1) relatively prime to e
         //
@@ -71,8 +76,8 @@
             for (;;)
             {
                 q = new BigInteger(qbitlength, 1, param.getRandom());
-                
-                if (q.equals(p))
+
+                if (q.subtract(p).abs().bitLength() < mindiffbits)
                 {
                     continue;
                 }
diff --git a/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java b/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java
deleted file mode 100644
index b0f3c0b..0000000
--- a/src/main/java/org/bouncycastle/crypto/macs/BlockCipherMac.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package org.bouncycastle.crypto.macs;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-
-public class BlockCipherMac
-    implements Mac
-{
-    private byte[]          mac;
-
-    private byte[]          buf;
-    private int             bufOff;
-    private BlockCipher     cipher;
-
-    private int             macSize;
-
-    /**
-     * create a standard MAC based on a block cipher. This will produce an
-     * authentication code half the length of the block size of the cipher.
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @deprecated use CBCBlockCipherMac
-     */
-    public BlockCipherMac(
-        BlockCipher     cipher)
-    {
-        this(cipher, (cipher.getBlockSize() * 8) / 2);
-    }
-
-    /**
-     * create a standard MAC based on a block cipher with the size of the
-     * MAC been given in bits.
-     * <p>
-     * Note: the size of the MAC must be at least 16 bits (FIPS Publication 113),
-     * and in general should be less than the size of the block cipher as it reduces
-     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
-     * @deprecated use CBCBlockCipherMac
-     */
-    public BlockCipherMac(
-        BlockCipher     cipher,
-        int             macSizeInBits)
-    {
-        if ((macSizeInBits % 8) != 0)
-        {
-            throw new IllegalArgumentException("MAC size must be multiple of 8");
-        }
-
-        this.cipher = new CBCBlockCipher(cipher);
-        this.macSize = macSizeInBits / 8;
-
-        mac = new byte[cipher.getBlockSize()];
-
-        buf = new byte[cipher.getBlockSize()];
-        bufOff = 0;
-    }
-
-    public String getAlgorithmName()
-    {
-        return cipher.getAlgorithmName();
-    }
-
-    public void init(
-        CipherParameters    params)
-    {
-        reset();
-
-        cipher.init(true, params);
-    }
-
-    public int getMacSize()
-    {
-        return macSize;
-    }
-
-    public void update(
-        byte        in)
-    {
-        int         resultLen = 0;
-
-        if (bufOff == buf.length)
-        {
-            resultLen = cipher.processBlock(buf, 0, mac, 0);
-            bufOff = 0;
-        }
-
-        buf[bufOff++] = in;
-    }
-
-    public void update(
-        byte[]      in,
-        int         inOff,
-        int         len)
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        int blockSize = cipher.getBlockSize();
-        int resultLen = 0;
-        int gapLen = blockSize - bufOff;
-
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-
-            resultLen += cipher.processBlock(buf, 0, mac, 0);
-
-            bufOff = 0;
-            len -= gapLen;
-            inOff += gapLen;
-
-            while (len > blockSize)
-            {
-                resultLen += cipher.processBlock(in, inOff, mac, 0);
-
-                len -= blockSize;
-                inOff += blockSize;
-            }
-        }
-
-        System.arraycopy(in, inOff, buf, bufOff, len);
-
-        bufOff += len;
-    }
-
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
-    {
-        int blockSize = cipher.getBlockSize();
-
-        //
-        // pad with zeroes
-        //
-        while (bufOff < blockSize)
-        {
-            buf[bufOff] = 0;
-            bufOff++;
-        }
-
-        cipher.processBlock(buf, 0, mac, 0);
-
-        System.arraycopy(mac, 0, out, outOff, macSize);
-
-        reset();
-
-        return macSize;
-    }
-
-    /**
-     * Reset the mac generator.
-     */
-    public void reset()
-    {
-        /*
-         * clean the buffer.
-         */
-        for (int i = 0; i < buf.length; i++)
-        {
-            buf[i] = 0;
-        }
-
-        bufOff = 0;
-
-        /*
-         * reset the underlying cipher.
-         */
-        cipher.reset();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
index e283df2..9bf6cb0 100644
--- a/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
+++ b/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
@@ -123,11 +123,9 @@
     public void update(
         byte        in)
     {
-        int         resultLen = 0;
-
         if (bufOff == buf.length)
         {
-            resultLen = cipher.processBlock(buf, 0, mac, 0);
+            cipher.processBlock(buf, 0, mac, 0);
             bufOff = 0;
         }
 
@@ -145,14 +143,13 @@
         }
 
         int blockSize = cipher.getBlockSize();
-        int resultLen = 0;
         int gapLen = blockSize - bufOff;
 
         if (len > gapLen)
         {
             System.arraycopy(in, inOff, buf, bufOff, gapLen);
 
-            resultLen += cipher.processBlock(buf, 0, mac, 0);
+            cipher.processBlock(buf, 0, mac, 0);
 
             bufOff = 0;
             len -= gapLen;
@@ -160,7 +157,7 @@
 
             while (len > blockSize)
             {
-                resultLen += cipher.processBlock(in, inOff, mac, 0);
+                cipher.processBlock(in, inOff, mac, 0);
 
                 len -= blockSize;
                 inOff += blockSize;
diff --git a/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java b/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
deleted file mode 100644
index 1a0395e..0000000
--- a/src/main/java/org/bouncycastle/crypto/macs/CFBBlockCipherMac.java
+++ /dev/null
@@ -1,390 +0,0 @@
-package org.bouncycastle.crypto.macs;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.paddings.BlockCipherPadding;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-
-/**
- * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
- */
-class MacCFBBlockCipher
-{
-    private byte[]          IV;
-    private byte[]          cfbV;
-    private byte[]          cfbOutV;
-
-    private int                 blockSize;
-    private BlockCipher         cipher = null;
-
-    /**
-     * Basic constructor.
-     *
-     * @param cipher the block cipher to be used as the basis of the
-     * feedback mode.
-     * @param blockSize the block size in bits (note: a multiple of 8)
-     */
-    public MacCFBBlockCipher(
-        BlockCipher         cipher,
-        int                 bitBlockSize)
-    {
-        this.cipher = cipher;
-        this.blockSize = bitBlockSize / 8;
-
-        this.IV = new byte[cipher.getBlockSize()];
-        this.cfbV = new byte[cipher.getBlockSize()];
-        this.cfbOutV = new byte[cipher.getBlockSize()];
-    }
-
-    /**
-     * Initialise the cipher and, possibly, the initialisation vector (IV).
-     * If an IV isn't passed as part of the parameter, the IV will be all zeros.
-     * An IV which is too short is handled in FIPS compliant fashion.
-     *
-     * @param param the key and other data required by the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
-     */
-    public void init(
-        CipherParameters    params)
-        throws IllegalArgumentException
-    {
-        if (params instanceof ParametersWithIV)
-        {
-                ParametersWithIV ivParam = (ParametersWithIV)params;
-                byte[]      iv = ivParam.getIV();
-
-                if (iv.length < IV.length)
-                {
-                    System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
-                }
-                else
-                {
-                    System.arraycopy(iv, 0, IV, 0, IV.length);
-                }
-
-                reset();
-
-                cipher.init(true, ivParam.getParameters());
-        }
-        else
-        {
-                reset();
-
-                cipher.init(true, params);
-        }
-    }
-
-    /**
-     * return the algorithm name and mode.
-     *
-     * @return the name of the underlying algorithm followed by "/CFB"
-     * and the block size in bits.
-     */
-    public String getAlgorithmName()
-    {
-        return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
-    }
-
-    /**
-     * return the block size we are operating at.
-     *
-     * @return the block size we are operating at (in bytes).
-     */
-    public int getBlockSize()
-    {
-        return blockSize;
-    }
-
-    /**
-     * Process one block of input from the array in and write it to
-     * the out array.
-     *
-     * @param in the array containing the input data.
-     * @param inOff offset into the in array the data starts at.
-     * @param out the array the output data will be copied into.
-     * @param outOff the offset into the out array the output will start at.
-     * @exception DataLengthException if there isn't enough data in in, or
-     * space in out.
-     * @exception IllegalStateException if the cipher isn't initialised.
-     * @return the number of bytes processed and produced.
-     */
-    public int processBlock(
-        byte[]      in,
-        int         inOff,
-        byte[]      out,
-        int         outOff)
-        throws DataLengthException, IllegalStateException
-    {
-        if ((inOff + blockSize) > in.length)
-        {
-            throw new DataLengthException("input buffer too short");
-        }
-
-        if ((outOff + blockSize) > out.length)
-        {
-            throw new DataLengthException("output buffer too short");
-        }
-
-        cipher.processBlock(cfbV, 0, cfbOutV, 0);
-
-        //
-        // XOR the cfbV with the plaintext producing the cipher text
-        //
-        for (int i = 0; i < blockSize; i++)
-        {
-            out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
-        }
-
-        //
-        // change over the input block.
-        //
-        System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
-        System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
-
-        return blockSize;
-    }
-
-    /**
-     * reset the chaining vector back to the IV and reset the underlying
-     * cipher.
-     */
-    public void reset()
-    {
-        System.arraycopy(IV, 0, cfbV, 0, IV.length);
-
-        cipher.reset();
-    }
-
-    void getMacBlock(
-        byte[]  mac)
-    {
-        cipher.processBlock(cfbV, 0, mac, 0);
-    }
-}
-
-public class CFBBlockCipherMac
-    implements Mac
-{
-    private byte[]              mac;
-
-    private byte[]              buf;
-    private int                 bufOff;
-    private MacCFBBlockCipher   cipher;
-    private BlockCipherPadding  padding = null;
-
-
-    private int                 macSize;
-
-    /**
-     * create a standard MAC based on a CFB block cipher. This will produce an
-     * authentication code half the length of the block size of the cipher, with
-     * the CFB mode set to 8 bits.
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     */
-    public CFBBlockCipherMac(
-        BlockCipher     cipher)
-    {
-        this(cipher, 8, (cipher.getBlockSize() * 8) / 2, null);
-    }
-
-    /**
-     * create a standard MAC based on a CFB block cipher. This will produce an
-     * authentication code half the length of the block size of the cipher, with
-     * the CFB mode set to 8 bits.
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param padding the padding to be used.
-     */
-    public CFBBlockCipherMac(
-        BlockCipher         cipher,
-        BlockCipherPadding  padding)
-    {
-        this(cipher, 8, (cipher.getBlockSize() * 8) / 2, padding);
-    }
-
-    /**
-     * create a standard MAC based on a block cipher with the size of the
-     * MAC been given in bits. This class uses CFB mode as the basis for the
-     * MAC generation.
-     * <p>
-     * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
-     * or 16 bits if being used as a data authenticator (FIPS Publication 113),
-     * and in general should be less than the size of the block cipher as it reduces
-     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param cfbBitSize the size of an output block produced by the CFB mode.
-     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
-     */
-    public CFBBlockCipherMac(
-        BlockCipher         cipher,
-        int                 cfbBitSize,
-        int                 macSizeInBits)
-    {
-        this(cipher, cfbBitSize, macSizeInBits, null);
-    }
-
-    /**
-     * create a standard MAC based on a block cipher with the size of the
-     * MAC been given in bits. This class uses CFB mode as the basis for the
-     * MAC generation.
-     * <p>
-     * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
-     * or 16 bits if being used as a data authenticator (FIPS Publication 113),
-     * and in general should be less than the size of the block cipher as it reduces
-     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param cfbBitSize the size of an output block produced by the CFB mode.
-     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
-     * @param padding a padding to be used.
-     */
-    public CFBBlockCipherMac(
-        BlockCipher         cipher,
-        int                 cfbBitSize,
-        int                 macSizeInBits,
-        BlockCipherPadding  padding)
-    {
-        if ((macSizeInBits % 8) != 0)
-        {
-            throw new IllegalArgumentException("MAC size must be multiple of 8");
-        }
-
-        mac = new byte[cipher.getBlockSize()];
-
-        this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
-        this.padding = padding;
-        this.macSize = macSizeInBits / 8;
-
-        buf = new byte[this.cipher.getBlockSize()];
-        bufOff = 0;
-    }
-
-    public String getAlgorithmName()
-    {
-        return cipher.getAlgorithmName();
-    }
-
-    public void init(
-        CipherParameters    params)
-    {
-        reset();
-
-        cipher.init(params);
-    }
-
-    public int getMacSize()
-    {
-        return macSize;
-    }
-
-    public void update(
-        byte        in)
-    {
-        int         resultLen = 0;
-
-        if (bufOff == buf.length)
-        {
-            resultLen = cipher.processBlock(buf, 0, mac, 0);
-            bufOff = 0;
-        }
-
-        buf[bufOff++] = in;
-    }
-
-    public void update(
-        byte[]      in,
-        int         inOff,
-        int         len)
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        int blockSize = cipher.getBlockSize();
-        int resultLen = 0;
-        int gapLen = blockSize - bufOff;
-
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-
-            resultLen += cipher.processBlock(buf, 0, mac, 0);
-
-            bufOff = 0;
-            len -= gapLen;
-            inOff += gapLen;
-
-            while (len > blockSize)
-            {
-                resultLen += cipher.processBlock(in, inOff, mac, 0);
-
-                len -= blockSize;
-                inOff += blockSize;
-            }
-        }
-
-        System.arraycopy(in, inOff, buf, bufOff, len);
-
-        bufOff += len;
-    }
-
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
-    {
-        int blockSize = cipher.getBlockSize();
-
-        //
-        // pad with zeroes
-        //
-        if (this.padding == null)
-        {
-            while (bufOff < blockSize)
-            {
-                buf[bufOff] = 0;
-                bufOff++;
-            }
-        }
-        else
-        {
-            padding.addPadding(buf, bufOff);
-        }
-
-        cipher.processBlock(buf, 0, mac, 0);
-
-        cipher.getMacBlock(mac);
-
-        System.arraycopy(mac, 0, out, outOff, macSize);
-
-        reset();
-
-        return macSize;
-    }
-
-    /**
-     * Reset the mac generator.
-     */
-    public void reset()
-    {
-        /*
-         * clean the buffer.
-         */
-        for (int i = 0; i < buf.length; i++)
-        {
-            buf[i] = 0;
-        }
-
-        bufOff = 0;
-
-        /*
-         * reset the underlying cipher.
-         */
-        cipher.reset();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/macs/CMac.java b/src/main/java/org/bouncycastle/crypto/macs/CMac.java
new file mode 100644
index 0000000..c5bc504
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/macs/CMac.java
@@ -0,0 +1,237 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+
+/**
+ * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
+ * <p>
+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
+ * </p><p>
+ * CMAC is a NIST recomendation - see 
+ * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ * </p><p>
+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
+ * analyzed by Tetsu Iwata and Kaoru Kurosawa.
+ * </p><p>
+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message 
+ * Authentication Code). OMAC stands for One-Key CBC MAC.
+ * </p><p>
+ * It supports 128- or 64-bits block ciphers, with any key size, and returns
+ * a MAC with dimension less or equal to the block size of the underlying 
+ * cipher.
+ * </p>
+ */
+public class CMac implements Mac
+{
+    private static final byte CONSTANT_128 = (byte)0x87;
+    private static final byte CONSTANT_64 = (byte)0x1b;
+
+    private byte[] ZEROES;
+
+    private byte[] mac;
+
+    private byte[] buf;
+    private int bufOff;
+    private BlockCipher cipher;
+
+    private int macSize;
+
+    private byte[] L, Lu, Lu2;
+
+    /**
+     * create a standard MAC based on a CBC block cipher (64 or 128 bit block).
+     * This will produce an authentication code the length of the block size
+     * of the cipher.
+     *
+     * @param cipher the cipher to be used as the basis of the MAC generation.
+     */
+    public CMac(BlockCipher cipher)
+    {
+        this(cipher, cipher.getBlockSize() * 8);
+    }
+
+    /**
+     * create a standard MAC based on a block cipher with the size of the
+     * MAC been given in bits.
+     * <p/>
+     * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
+     * or 16 bits if being used as a data authenticator (FIPS Publication 113),
+     * and in general should be less than the size of the block cipher as it reduces
+     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
+     *
+     * @param cipher        the cipher to be used as the basis of the MAC generation.
+     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and <= 128.
+     */
+    public CMac(BlockCipher cipher, int macSizeInBits)
+    {
+        if ((macSizeInBits % 8) != 0)
+        {
+            throw new IllegalArgumentException("MAC size must be multiple of 8");
+        }
+
+        if (macSizeInBits > (cipher.getBlockSize() * 8))
+        {
+            throw new IllegalArgumentException(
+                "MAC size must be less or equal to "
+                    + (cipher.getBlockSize() * 8));
+        }
+
+        if (cipher.getBlockSize() != 8 && cipher.getBlockSize() != 16)
+        {
+            throw new IllegalArgumentException(
+                "Block size must be either 64 or 128 bits");
+        }
+
+        this.cipher = new CBCBlockCipher(cipher);
+        this.macSize = macSizeInBits / 8;
+
+        mac = new byte[cipher.getBlockSize()];
+
+        buf = new byte[cipher.getBlockSize()];
+
+        ZEROES = new byte[cipher.getBlockSize()];
+
+        bufOff = 0;
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName();
+    }
+
+    private byte[] doubleLu(byte[] in)
+    {
+        int FirstBit = (in[0] & 0xFF) >> 7;
+        byte[] ret = new byte[in.length];
+        for (int i = 0; i < in.length - 1; i++)
+        {
+            ret[i] = (byte)((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7));
+        }
+        ret[in.length - 1] = (byte)(in[in.length - 1] << 1);
+        if (FirstBit == 1)
+        {
+            ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128 : CONSTANT_64;
+        }
+        return ret;
+    }
+
+    public void init(CipherParameters params)
+    {
+        reset();
+
+        cipher.init(true, params);
+
+        //initializes the L, Lu, Lu2 numbers
+        L = new byte[ZEROES.length];
+        cipher.processBlock(ZEROES, 0, L, 0);
+        Lu = doubleLu(L);
+        Lu2 = doubleLu(Lu);
+
+        cipher.init(true, params);
+    }
+
+    public int getMacSize()
+    {
+        return macSize;
+    }
+
+    public void update(byte in)
+    {
+        if (bufOff == buf.length)
+        {
+            cipher.processBlock(buf, 0, mac, 0);
+            bufOff = 0;
+        }
+
+        buf[bufOff++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException(
+                "Can't have a negative input length!");
+        }
+
+        int blockSize = cipher.getBlockSize();
+        int gapLen = blockSize - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            cipher.processBlock(buf, 0, mac, 0);
+
+            bufOff = 0;
+            len -= gapLen;
+            inOff += gapLen;
+
+            while (len > blockSize)
+            {
+                cipher.processBlock(in, inOff, mac, 0);
+
+                len -= blockSize;
+                inOff += blockSize;
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        int blockSize = cipher.getBlockSize();
+
+        byte[] lu;
+        if (bufOff == blockSize)
+        {
+            lu = Lu;
+        }
+        else
+        {
+            new ISO7816d4Padding().addPadding(buf, bufOff);
+            lu = Lu2;
+        }
+
+        for (int i = 0; i < mac.length; i++)
+        {
+            buf[i] ^= lu[i];
+        }
+
+        cipher.processBlock(buf, 0, mac, 0);
+
+        System.arraycopy(mac, 0, out, outOff, macSize);
+
+        reset();
+
+        return macSize;
+    }
+
+    /**
+     * Reset the mac generator.
+     */
+    public void reset()
+    {
+        /*
+         * clean the buffer.
+         */
+        for (int i = 0; i < buf.length; i++)
+        {
+            buf[i] = 0;
+        }
+
+        bufOff = 0;
+
+        /*
+         * reset the underlying cipher.
+         */
+        cipher.reset();
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java b/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java
deleted file mode 100644
index 11db130..0000000
--- a/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package org.bouncycastle.crypto.macs;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.engines.DESEngine;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.paddings.BlockCipherPadding;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC)
- *
- * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base
- * class must be changed to protected  
- */
-
-public class ISO9797Alg3Mac 
-    implements Mac 
-{
-    private byte[]              mac;
-    
-    private byte[]              buf;
-    private int                 bufOff;
-    private BlockCipher         cipher;
-    private BlockCipherPadding  padding;
-    
-    private int                 macSize;
-    private KeyParameter        lastKey2;
-    private KeyParameter        lastKey3;
-    
-    /**
-     * create a Retail-MAC based on a CBC block cipher. This will produce an
-     * authentication code of the length of the block size of the cipher.
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation. This must
-     * be DESEngine.
-     */
-    public ISO9797Alg3Mac(
-            BlockCipher     cipher)
-    {
-        this(cipher, cipher.getBlockSize() * 8, null);
-    }
-    
-    /**
-     * create a Retail-MAC based on a CBC block cipher. This will produce an
-     * authentication code of the length of the block size of the cipher.
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param padding the padding to be used to complete the last block.
-     */
-    public ISO9797Alg3Mac(
-        BlockCipher         cipher,
-        BlockCipherPadding  padding)
-    {
-        this(cipher, cipher.getBlockSize() * 8, padding);
-    }
-
-    /**
-     * create a Retail-MAC based on a block cipher with the size of the
-     * MAC been given in bits. This class uses single DES CBC mode as the basis for the
-     * MAC generation.
-     * <p>
-     * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
-     * or 16 bits if being used as a data authenticator (FIPS Publication 113),
-     * and in general should be less than the size of the block cipher as it reduces
-     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
-     */
-    public ISO9797Alg3Mac(
-        BlockCipher     cipher,
-        int             macSizeInBits)
-    {
-        this(cipher, macSizeInBits, null);
-    }
-
-    /**
-     * create a standard MAC based on a block cipher with the size of the
-     * MAC been given in bits. This class uses single DES CBC mode as the basis for the
-     * MAC generation. The final block is decrypted and then encrypted using the
-     * middle and right part of the key.
-     * <p>
-     * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
-     * or 16 bits if being used as a data authenticator (FIPS Publication 113),
-     * and in general should be less than the size of the block cipher as it reduces
-     * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
-     *
-     * @param cipher the cipher to be used as the basis of the MAC generation.
-     * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
-     * @param padding the padding to be used to complete the last block.
-     */
-    public ISO9797Alg3Mac(
-        BlockCipher         cipher,
-        int                 macSizeInBits,
-        BlockCipherPadding  padding)
-    {
-        if ((macSizeInBits % 8) != 0)
-        {
-            throw new IllegalArgumentException("MAC size must be multiple of 8");
-        }
-
-        if (!(cipher instanceof DESEngine))
-        {
-            throw new IllegalArgumentException("cipher must be instance of DESEngine");
-        }
-
-        this.cipher = new CBCBlockCipher(cipher);
-        this.padding = padding;
-        this.macSize = macSizeInBits / 8;
-
-        mac = new byte[cipher.getBlockSize()];
-
-        buf = new byte[cipher.getBlockSize()];
-        bufOff = 0;
-    }
-    
-    public String getAlgorithmName()
-    {
-        return "ISO9797Alg3";
-    }
-
-    public void init(CipherParameters params)
-    {
-        reset();
-
-        if (!(params instanceof KeyParameter))
-        {
-            throw new IllegalArgumentException(
-                    "params must be an instance of KeyParameter");
-        }
-
-        // KeyParameter must contain a double or triple length DES key,
-        // however the underlying cipher is a single DES. The middle and
-        // right key are used only in the final step.
-
-        KeyParameter kp = (KeyParameter)params;
-        KeyParameter key1;
-        byte[] keyvalue = kp.getKey();
-
-        if (keyvalue.length == 16)
-        { // Double length DES key
-            key1 = new KeyParameter(keyvalue, 0, 8);
-            this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
-            this.lastKey3 = key1;
-        }
-        else if (keyvalue.length == 24)
-        { // Triple length DES key
-            key1 = new KeyParameter(keyvalue, 0, 8);
-            this.lastKey2 = new KeyParameter(keyvalue, 8, 8);
-            this.lastKey3 = new KeyParameter(keyvalue, 16, 8);
-        }
-        else
-        {
-            throw new IllegalArgumentException(
-                    "Key must be either 112 or 168 bit long");
-        }
-
-        cipher.init(true, key1);
-    }
-    
-    public int getMacSize()
-    {
-        return macSize;
-    }
-    
-    public void update(
-            byte        in)
-    {
-        int         resultLen = 0;
-        
-        if (bufOff == buf.length)
-        {
-            resultLen = cipher.processBlock(buf, 0, mac, 0);
-            bufOff = 0;
-        }
-        
-        buf[bufOff++] = in;
-    }
-    
-    
-    public void update(
-            byte[]      in,
-            int         inOff,
-            int         len)
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-        
-        int blockSize = cipher.getBlockSize();
-        int resultLen = 0;
-        int gapLen = blockSize - bufOff;
-        
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-            
-            resultLen += cipher.processBlock(buf, 0, mac, 0);
-            
-            bufOff = 0;
-            len -= gapLen;
-            inOff += gapLen;
-            
-            while (len > blockSize)
-            {
-                resultLen += cipher.processBlock(in, inOff, mac, 0);
-                
-                len -= blockSize;
-                inOff += blockSize;
-            }
-        }
-        
-        System.arraycopy(in, inOff, buf, bufOff, len);
-        
-        bufOff += len;
-    }
-    
-    public int doFinal(
-            byte[]  out,
-            int     outOff)
-    {
-        int blockSize = cipher.getBlockSize();
-        
-        if (padding == null)
-        {
-            //
-            // pad with zeroes
-            //
-            while (bufOff < blockSize)
-            {
-                buf[bufOff] = 0;
-                bufOff++;
-            }
-        }
-        else
-        {
-            if (bufOff == blockSize)
-            {
-                cipher.processBlock(buf, 0, mac, 0);
-                bufOff = 0;
-            }
-            
-            padding.addPadding(buf, bufOff);
-        }
-        
-        cipher.processBlock(buf, 0, mac, 0);
-
-        // Added to code from base class
-        DESEngine deseng = new DESEngine();
-        
-        deseng.init(false, this.lastKey2);
-        deseng.processBlock(mac, 0, mac, 0);
-        
-        deseng.init(true, this.lastKey3);
-        deseng.processBlock(mac, 0, mac, 0);
-        // ****
-        
-        System.arraycopy(mac, 0, out, outOff, macSize);
-        
-        reset();
-        
-        return macSize;
-    }
-
-    
-    /**
-     * Reset the mac generator.
-     */
-    public void reset()
-    {
-        /*
-         * clean the buffer.
-         */
-        for (int i = 0; i < buf.length; i++)
-        {
-            buf[i] = 0;
-        }
-        
-        bufOff = 0;
-        
-        /*
-         * reset the underlying cipher.
-         */
-        cipher.reset();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java b/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java
deleted file mode 100644
index 7463afd..0000000
--- a/src/main/java/org/bouncycastle/crypto/macs/OldHMac.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.bouncycastle.crypto.macs;
-
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * HMAC implementation based on RFC2104
- *
- * H(K XOR opad, H(K XOR ipad, text))
- */
-public class OldHMac
-implements Mac
-{
-    private final static int BLOCK_LENGTH = 64;
-
-    private final static byte IPAD = (byte)0x36;
-    private final static byte OPAD = (byte)0x5C;
-
-    private Digest digest;
-    private int digestSize;
-    private byte[] inputPad = new byte[BLOCK_LENGTH];
-    private byte[] outputPad = new byte[BLOCK_LENGTH];
-
-    /**
-     * @deprecated uses incorrect pad for SHA-512 and SHA-384 use HMac.
-     */
-    public OldHMac(
-        Digest digest)
-    {
-        this.digest = digest;
-        digestSize = digest.getDigestSize();
-    }
-
-    public String getAlgorithmName()
-    {
-        return digest.getAlgorithmName() + "/HMAC";
-    }
-
-    public Digest getUnderlyingDigest()
-    {
-        return digest;
-    }
-
-    public void init(
-        CipherParameters params)
-    {
-        digest.reset();
-
-        byte[] key = ((KeyParameter)params).getKey();
-
-        if (key.length > BLOCK_LENGTH)
-        {
-            digest.update(key, 0, key.length);
-            digest.doFinal(inputPad, 0);
-            for (int i = digestSize; i < inputPad.length; i++)
-            {
-                inputPad[i] = 0;
-            }
-        }
-        else
-        {
-            System.arraycopy(key, 0, inputPad, 0, key.length);
-            for (int i = key.length; i < inputPad.length; i++)
-            {
-                inputPad[i] = 0;
-            }
-        }
-
-        outputPad = new byte[inputPad.length];
-        System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length);
-
-        for (int i = 0; i < inputPad.length; i++)
-        {
-            inputPad[i] ^= IPAD;
-        }
-
-        for (int i = 0; i < outputPad.length; i++)
-        {
-            outputPad[i] ^= OPAD;
-        }
-
-        digest.update(inputPad, 0, inputPad.length);
-    }
-
-    public int getMacSize()
-    {
-        return digestSize;
-    }
-
-    public void update(
-        byte in)
-    {
-        digest.update(in);
-    }
-
-    public void update(
-        byte[] in,
-        int inOff,
-        int len)
-    {
-        digest.update(in, inOff, len);
-    }
-
-    public int doFinal(
-        byte[] out,
-        int outOff)
-    {
-        byte[] tmp = new byte[digestSize];
-        digest.doFinal(tmp, 0);
-
-        digest.update(outputPad, 0, outputPad.length);
-        digest.update(tmp, 0, tmp.length);
-
-        int     len = digest.doFinal(out, outOff);
-
-        reset();
-
-        return len;
-    }
-
-    /**
-     * Reset the mac generator.
-     */
-    public void reset()
-    {
-        /*
-         * reset the underlying digest.
-         */
-        digest.reset();
-
-        /*
-         * reinitialize the digest.
-         */
-        digest.update(inputPad, 0, inputPad.length);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
new file mode 100644
index 0000000..3c3bf34
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data.
+ * @see org.bouncycastle.crypto.params.AEADParameters
+ */
+public interface AEADBlockCipher
+{
+    /**
+     * initialise the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object.
+     *
+     * @param forEncryption true if we are setting up for encryption, false otherwise.
+     * @param params the necessary parameters for the underlying cipher to be initialised.
+     * @exception IllegalArgumentException if the params argument is inappropriate.
+     */
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException;
+
+    /**
+     * Return the name of the algorithm.
+     * 
+     * @return the algorithm name.
+     */
+    public String getAlgorithmName();
+
+    /**
+     * return the cipher this object wraps.
+     *
+     * @return the cipher this object wraps.
+     */
+    public BlockCipher getUnderlyingCipher();
+
+    /**
+     * encrypt/decrypt a single byte.
+     *
+     * @param in the byte to be processed.
+     * @param out the output buffer the processed byte goes into.
+     * @param outOff the offset into the output byte array the processed data starts at.
+     * @return the number of bytes written to out.
+     * @exception DataLengthException if the output buffer is too small.
+     */
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException;
+
+    /**
+     * process a block of bytes from in putting the result into out.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset into the in array where the data to be processed starts.
+     * @param len the number of bytes to be processed.
+     * @param out the output buffer the processed bytes go into.
+     * @param outOff the offset into the output byte array the processed data starts at.
+     * @return the number of bytes written to out.
+     * @exception DataLengthException if the output buffer is too small.
+     */
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException;
+
+    /**
+     * Finish the operation either appending or verifying the MAC at the end of the data.
+     *
+     * @param out space for any resulting output data.
+     * @param outOff offset into out to start copying the data at.
+     * @return number of bytes written into out.
+     * @throws IllegalStateException if the cipher is in an inappropriate state.
+     * @throws org.bouncycastle.crypto.InvalidCipherTextException if the MAC fails to match.
+     */
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException;
+
+    /**
+     * Return the value of the MAC associated with the last stream processed.
+     *
+     * @return MAC for plaintext data.
+     */
+    public byte[] getMac();
+
+    /**
+     * return the size of the output buffer required for a processBytes
+     * an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to processBytes
+     * with len bytes of input.
+     */
+    public int getUpdateOutputSize(int len);
+
+    /**
+     * return the size of the output buffer required for a processBytes plus a
+     * doFinal with an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to processBytes and doFinal
+     * with len bytes of input.
+     */
+    public int getOutputSize(int len);
+
+    /**
+     * Reset the cipher. After resetting the cipher is in the same state
+     * as it was after the last init (if there was one).
+     */
+    public void reset();
+}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
index 7640045..f75c546 100644
--- a/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
 
 /**
  * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
@@ -136,6 +137,7 @@
     public void reset()
     {
         System.arraycopy(IV, 0, cbcV, 0, IV.length);
+        Arrays.fill(cbcNextV, (byte)0);
 
         cipher.reset();
     }
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index 95db190..bedc3d1 100644
--- a/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -1,12 +1,16 @@
 package org.bouncycastle.crypto.modes;
 
+import java.io.ByteArrayOutputStream;
+
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
-import org.bouncycastle.crypto.params.CCMParameters;
+import org.bouncycastle.crypto.params.AEADParameters;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
@@ -15,13 +19,17 @@
  * <b>Note</b>: this mode is a packet mode - it needs all the data up front.
  */
 public class CCMBlockCipher
+    implements AEADBlockCipher
 {
-    private BlockCipher     cipher = null;
-    private int             blockSize;
-    private boolean         forEncryption;
-    private CCMParameters   params;
-    private byte[]          macBlock;
-
+    private BlockCipher           cipher;
+    private int                   blockSize;
+    private boolean               forEncryption;
+    private byte[]                nonce;
+    private byte[]                associatedText;
+    private int                   macSize;
+    private CipherParameters      keyParam;
+    private byte[]                macBlock;
+    private ByteArrayOutputStream data = new ByteArrayOutputStream();
 
     /**
      * Basic constructor.
@@ -54,20 +62,72 @@
     public void init(boolean forEncryption, CipherParameters params)
           throws IllegalArgumentException
     {
-        if (!(params instanceof CCMParameters))
-        {
-            throw new IllegalArgumentException("parameters need to be CCMParameters");
-        }
-        
         this.forEncryption = forEncryption;
-        this.params = (CCMParameters)params;
+
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters param = (AEADParameters)params;
+
+            nonce = param.getNonce();
+            associatedText = param.getAssociatedText();
+            macSize = param.getMacSize() / 8;
+            keyParam = param.getKey();
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV param = (ParametersWithIV)params;
+
+            nonce = param.getIV();
+            associatedText = null;
+            macSize = macBlock.length / 2;
+            keyParam = param.getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to CCM");
+        }
     }
 
     public String getAlgorithmName()
     {
         return cipher.getAlgorithmName() + "/CCM";
     }
-    
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        data.write(in);
+
+        return 0;
+    }
+
+    public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        data.write(in, inOff, inLen);
+
+        return 0;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        byte[] text = data.toByteArray();
+        byte[] enc = processPacket(text, 0, text.length);
+
+        System.arraycopy(enc, 0, out, outOff, enc.length);
+
+        reset();
+
+        return enc.length;
+    }
+
+    public void reset()
+    {
+        cipher.reset();
+        data.reset();
+    }
+
     /**
      * Returns a byte array containing the mac calculated as part of the
      * last encrypt or decrypt operation.
@@ -76,32 +136,47 @@
      */
     public byte[] getMac()
     {
-        byte[] mac = new byte[params.getMacSize() / 8];
+        byte[] mac = new byte[macSize];
         
         System.arraycopy(macBlock, 0, mac, 0, mac.length);
         
         return mac;
     }
 
+    public int getUpdateOutputSize(int len)
+    {
+        return 0;
+    }
+
+    public int getOutputSize(int len)
+    {
+        if (forEncryption)
+        {
+            return data.size() + len + macSize;
+        }
+        else
+        {
+            return data.size() + len - macSize;
+        }
+    }
+
     public byte[] processPacket(byte[] in, int inOff, int inLen)
         throws IllegalStateException, InvalidCipherTextException
     {
-        if (params == null)
+        if (keyParam == null)
         {
             throw new IllegalStateException("CCM cipher unitialized.");
         }
         
         BlockCipher ctrCipher = new SICBlockCipher(cipher);
         byte[] iv = new byte[blockSize];
-        byte[] nonce = params.getNonce();
-        int    macSize = params.getMacSize() / 8;
         byte[] out;
 
         iv[0] = (byte)(((15 - nonce.length) - 1) & 0x7);
         
         System.arraycopy(nonce, 0, iv, 1, nonce.length);
         
-        ctrCipher.init(forEncryption, new ParametersWithIV(params.getKey(), iv));
+        ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
         
         if (forEncryption)
         {
@@ -168,7 +243,7 @@
             
             calculateMac(out, 0, out.length, calculatedMacBlock);
             
-            if (!areEqual(macBlock, calculatedMacBlock))
+            if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
             {
                 throw new InvalidCipherTextException("mac check in CCM failed");
             }
@@ -179,19 +254,16 @@
     
     private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
     {
-        Mac    cMac = new CBCBlockCipherMac(cipher, params.getMacSize());
+        Mac    cMac = new CBCBlockCipherMac(cipher, macSize * 8);
 
-        byte[] nonce = params.getNonce();
-        byte[] associatedText = params.getAssociatedText();
-        
-        cMac.init(params.getKey());
+        cMac.init(keyParam);
 
         //
         // build b0
         //
         byte[] b0 = new byte[16];
     
-        if (associatedText != null && associatedText.length != 0)
+        if (hasAssociatedText())
         {
             b0[0] |= 0x40;
         }
@@ -216,7 +288,7 @@
         //
         // process associated text
         //
-        if (associatedText != null)
+        if (hasAssociatedText())
         {
             int extra;
             
@@ -258,27 +330,9 @@
 
         return cMac.doFinal(macBlock, 0);
     }
-    
-    /**
-     * compare two byte arrays.
-     */
-    private boolean areEqual(
-        byte[]    a,
-        byte[]    b)
+
+    private boolean hasAssociatedText()
     {
-        if (a.length != b.length)
-        {
-            return false;
-        }
-        
-        for (int i = 0; i != b.length; i++)
-        {
-            if (a[i] != b[i])
-            {
-                return false;
-            }
-        }
-        
-        return true;
+        return associatedText != null && associatedText.length != 0;
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index 0f64f04..0de0450 100644
--- a/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -173,7 +173,7 @@
         cipher.processBlock(cfbV, 0, cfbOutV, 0);
 
         //
-        // XOR the cfbV with the plaintext producing the cipher text
+        // XOR the cfbV with the plaintext producing the ciphertext
         //
         for (int i = 0; i < blockSize; i++)
         {
@@ -227,7 +227,7 @@
         System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize);
 
         //
-        // XOR the cfbV with the plaintext producing the plain text
+        // XOR the cfbV with the ciphertext producing the plaintext
         //
         for (int i = 0; i < blockSize; i++)
         {
diff --git a/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
new file mode 100644
index 0000000..327026e
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
@@ -0,0 +1,304 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and 
+ * Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
+ * 
+ * http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+ * 
+ * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block 
+ * cipher to encrypt and authenticate data. It's on-line (the length of a 
+ * message isn't needed to begin processing it), has good performances, it's
+ * simple and provably secure (provided the underlying block cipher is secure).
+ * 
+ * Of course, this implementations is NOT thread-safe.
+ */
+public class EAXBlockCipher
+    implements AEADBlockCipher
+{
+    private static final byte nTAG = 0x0;
+
+    private static final byte hTAG = 0x1;
+
+    private static final byte cTAG = 0x2;
+
+    private SICBlockCipher cipher;
+
+    private boolean forEncryption;
+
+    private int blockSize;
+
+    private Mac mac;
+
+    private byte[] nonceMac;
+    private byte[] associatedTextMac;
+    private byte[] macBlock;
+    
+    private int macSize;
+    private byte[] bufBlock;
+    private int bufOff;
+
+    /**
+     * Constructor that accepts an instance of a block cipher engine.
+     *
+     * @param cipher the engine to use
+     */
+    public EAXBlockCipher(BlockCipher cipher)
+    {
+        blockSize = cipher.getBlockSize();
+        mac = new CMac(cipher);
+        macBlock = new byte[blockSize];
+        bufBlock = new byte[blockSize * 2];
+        associatedTextMac = new byte[mac.getMacSize()];
+        nonceMac = new byte[mac.getMacSize()];
+        this.cipher = new SICBlockCipher(cipher);
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getUnderlyingCipher().getAlgorithmName() + "/EAX";
+    }
+
+    public BlockCipher getUnderlyingCipher()
+    {
+        return cipher.getUnderlyingCipher();
+    }
+
+    public int getBlockSize()
+    {
+        return cipher.getBlockSize();
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+
+        byte[] nonce, associatedText;
+        CipherParameters keyParam;
+
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters param = (AEADParameters)params;
+
+            nonce = param.getNonce();
+            associatedText = param.getAssociatedText();
+            macSize = param.getMacSize() / 8;
+            keyParam = param.getKey();
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV param = (ParametersWithIV)params;
+
+            nonce = param.getIV();
+            associatedText = new byte[0];
+            macSize = mac.getMacSize() / 2;
+            keyParam = param.getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to EAX");
+        }
+
+        byte[] tag = new byte[blockSize];
+
+        mac.init(keyParam);
+        tag[blockSize - 1] = hTAG;
+        mac.update(tag, 0, blockSize);
+        mac.update(associatedText, 0, associatedText.length);
+        mac.doFinal(associatedTextMac, 0);
+
+        tag[blockSize - 1] = nTAG;
+        mac.update(tag, 0, blockSize);
+        mac.update(nonce, 0, nonce.length);
+        mac.doFinal(nonceMac, 0);
+
+        tag[blockSize - 1] = cTAG;
+        mac.update(tag, 0, blockSize);
+
+        cipher.init(true, new ParametersWithIV(keyParam, nonceMac));
+    }
+
+    private void calculateMac()
+    {
+        byte[] outC = new byte[blockSize];
+        mac.doFinal(outC, 0);
+
+        for (int i = 0; i < macBlock.length; i++)
+        {
+            macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
+        }
+    }
+
+    public void reset()
+    {
+        reset(true);
+    }
+
+    private void reset(
+        boolean clearMac)
+    {
+        cipher.reset();
+        mac.reset();
+
+        bufOff = 0;
+        Arrays.fill(bufBlock, (byte)0);
+
+        if (clearMac)
+        {
+            Arrays.fill(macBlock, (byte)0);
+        }
+
+        byte[] tag = new byte[blockSize];
+        tag[blockSize - 1] = cTAG;
+        mac.update(tag, 0, blockSize);
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        return process(in, out, outOff);
+    }
+
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        int resultLen = 0;
+
+        for (int i = 0; i != len; i++)
+        {
+            resultLen += process(in[inOff + i], out, outOff + resultLen);
+        }
+
+        return resultLen;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        int extra = bufOff;
+        byte[] tmp = new byte[bufBlock.length];
+
+        bufOff = 0;
+
+        if (forEncryption)
+        {
+            cipher.processBlock(bufBlock, 0, tmp, 0);
+            cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+            System.arraycopy(tmp, 0, out, outOff, extra);
+
+            mac.update(tmp, 0, extra);
+
+            calculateMac();
+
+            System.arraycopy(macBlock, 0, out, outOff + extra, macSize);
+
+            reset(false);
+
+            return extra + macSize;
+        }
+        else
+        {
+            if (extra > macSize)
+            {
+                mac.update(bufBlock, 0, extra - macSize);
+
+                cipher.processBlock(bufBlock, 0, tmp, 0);
+                cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
+
+                System.arraycopy(tmp, 0, out, outOff, extra - macSize);
+            }
+
+            calculateMac();
+
+            if (!verifyMac(bufBlock, extra - macSize))
+            {
+                throw new InvalidCipherTextException("mac check in EAX failed");
+            }
+
+            reset(false);
+
+            return extra - macSize;
+        }
+    }
+
+    public byte[] getMac()
+    {
+        byte[] mac = new byte[macSize];
+
+        System.arraycopy(macBlock, 0, mac, 0, macSize);
+
+        return mac;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        return ((len + bufOff) / blockSize) * blockSize;
+    }
+
+    public int getOutputSize(int len)
+    {
+        if (forEncryption)
+        {
+             return len + bufOff + macSize;
+        }
+        else
+        {
+             return len + bufOff - macSize;
+        }
+    }
+
+    private int process(byte b, byte[] out, int outOff)
+    {
+        bufBlock[bufOff++] = b;
+
+        if (bufOff == bufBlock.length)
+        {
+            int size;
+
+            if (forEncryption)
+            {
+                size = cipher.processBlock(bufBlock, 0, out, outOff);
+
+                mac.update(out, outOff, blockSize);
+            }
+            else
+            {
+                mac.update(bufBlock, 0, blockSize);
+
+                size = cipher.processBlock(bufBlock, 0, out, outOff);
+            }
+
+            bufOff = blockSize;
+            System.arraycopy(bufBlock, blockSize, bufBlock, 0, blockSize);
+
+            return size;
+        }
+
+        return 0;
+    }
+
+    private boolean verifyMac(byte[] mac, int off)
+    {
+        for (int i = 0; i < macSize; i++)
+        {
+            if (macBlock[i] != mac[off + i])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
new file mode 100644
index 0000000..8e7c315
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -0,0 +1,416 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Implements the Galois/Counter mode (GCM) detailed in
+ * NIST Special Publication 800-38D.
+ */
+public class GCMBlockCipher
+    implements AEADBlockCipher
+{
+    private static final int BLOCK_SIZE = 16;
+    private static final byte[] ZEROES = new byte[BLOCK_SIZE];
+
+    // not final due to a compiler bug 
+    private BlockCipher   cipher;
+    private GCMMultiplier multiplier;
+
+    // These fields are set by init and not modified by processing
+    private boolean             forEncryption;
+    private int                 macSize;
+    private byte[]              nonce;
+    private byte[]              A;
+    private KeyParameter        keyParam;
+    private byte[]              H;
+    private byte[]              initS;
+    private byte[]              J0;
+
+    // These fields are modified during processing
+    private byte[]      bufBlock;
+    private byte[]      macBlock;
+    private byte[]      S;
+    private byte[]      counter;
+    private int         bufOff;
+    private long        totalLength;
+
+    public GCMBlockCipher(BlockCipher c)
+    {
+        this(c, null);
+    }
+
+    public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
+    {
+        if (c.getBlockSize() != BLOCK_SIZE)
+        {
+            throw new IllegalArgumentException(
+                "cipher required with a block size of " + BLOCK_SIZE + ".");
+        }
+
+        if (m == null)
+        {
+            // TODO Consider a static property specifying default multiplier
+            m = new Tables8kGCMMultiplier();
+        }
+
+        this.cipher = c;
+        this.multiplier = m;
+    }
+
+    public BlockCipher getUnderlyingCipher()
+    {
+        return cipher;
+    }
+
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName() + "/GCM";
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+        this.macBlock = null;
+
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters param = (AEADParameters)params;
+
+            nonce = param.getNonce();
+            A = param.getAssociatedText();
+
+            int macSizeBits = param.getMacSize();
+            if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+            {
+                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+            }
+
+            macSize = macSizeBits / 8; 
+            keyParam = param.getKey();
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV param = (ParametersWithIV)params;
+
+            nonce = param.getIV();
+            A = null;
+            macSize = 16;
+            keyParam = (KeyParameter)param.getParameters();
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to GCM");
+        }
+
+        int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize); 
+        this.bufBlock = new byte[bufLength];
+
+        if (nonce == null || nonce.length < 1)
+        {
+            throw new IllegalArgumentException("IV must be at least 1 byte");
+        }
+
+        if (A == null)
+        {
+            // Avoid lots of null checks
+            A = new byte[0];
+        }
+
+        // Cipher always used in forward mode
+        cipher.init(true, keyParam);
+
+        // TODO This should be configurable by init parameters
+        // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
+//        this.tagLength = 16;
+
+        this.H = new byte[BLOCK_SIZE];
+        cipher.processBlock(ZEROES, 0, H, 0);
+        multiplier.init(H);
+
+        this.initS = gHASH(A);
+
+        if (nonce.length == 12)
+        {
+            this.J0 = new byte[16];
+            System.arraycopy(nonce, 0, J0, 0, nonce.length);
+            this.J0[15] = 0x01;
+        }
+        else
+        {
+            this.J0 = gHASH(nonce);
+            byte[] X = new byte[16];
+            packLength((long)nonce.length * 8, X, 8);
+            xor(this.J0, X);
+            multiplier.multiplyH(this.J0);
+        }
+
+        this.S = Arrays.clone(initS);
+        this.counter = Arrays.clone(J0);
+        this.bufOff = 0;
+        this.totalLength = 0;
+    }
+
+    public byte[] getMac()
+    {
+        return Arrays.clone(macBlock);
+    }
+
+    public int getOutputSize(int len)
+    {
+        if (forEncryption)
+        {
+             return len + bufOff + macSize;
+        }
+
+        return len + bufOff - macSize;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        return ((len + bufOff) / BLOCK_SIZE) * BLOCK_SIZE;
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        return process(in, out, outOff);
+    }
+
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        int resultLen = 0;
+
+        for (int i = 0; i != len; i++)
+        {
+//            resultLen += process(in[inOff + i], out, outOff + resultLen);
+            bufBlock[bufOff++] = in[inOff + i];
+
+            if (bufOff == bufBlock.length)
+            {
+                gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff + resultLen);
+                if (!forEncryption)
+                {
+                    System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+                }
+//              bufOff = 0;
+                bufOff = bufBlock.length - BLOCK_SIZE;
+//              return bufBlock.Length;
+                resultLen += BLOCK_SIZE;
+            }
+        }
+
+        return resultLen;
+    }
+
+    private int process(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        bufBlock[bufOff++] = in;
+
+        if (bufOff == bufBlock.length)
+        {
+            gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff);
+            if (!forEncryption)
+            {
+                System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+            }
+//            bufOff = 0;
+            bufOff = bufBlock.length - BLOCK_SIZE;
+//            return bufBlock.length;
+            return BLOCK_SIZE;
+        }
+
+        return 0;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+        throws IllegalStateException, InvalidCipherTextException
+    {
+        int extra = bufOff;
+        if (!forEncryption)
+        {
+            if (extra < macSize)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            extra -= macSize;
+        }
+
+        if (extra > 0)
+        {
+            byte[] tmp = new byte[BLOCK_SIZE];
+            System.arraycopy(bufBlock, 0, tmp, 0, extra);
+            gCTRBlock(tmp, extra, out, outOff);
+        }
+
+        // Final gHASH
+        byte[] X = new byte[16];
+        packLength((long)A.length * 8, X, 0);
+        packLength(totalLength * 8, X, 8);
+
+        xor(S, X);
+        multiplier.multiplyH(S);
+
+        // TODO Fix this if tagLength becomes configurable
+        // T = MSBt(GCTRk(J0,S))
+        byte[] tag = new byte[BLOCK_SIZE];
+        cipher.processBlock(J0, 0, tag, 0);
+        xor(tag, S);
+
+        int resultLen = extra;
+
+        // We place into macBlock our calculated value for T
+        this.macBlock = new byte[macSize];
+        System.arraycopy(tag, 0, macBlock, 0, macSize);
+
+        if (forEncryption)
+        {
+            // Append T to the message
+            System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize);
+            resultLen += macSize;
+        }
+        else
+        {
+            // Retrieve the T value from the message and compare to calculated one
+            byte[] msgMac = new byte[macSize];
+            System.arraycopy(bufBlock, extra, msgMac, 0, macSize);
+            if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac))
+            {
+                throw new InvalidCipherTextException("mac check in GCM failed");
+            }
+        }
+
+        reset(false);
+
+        return resultLen;
+    }
+
+    public void reset()
+    {
+        reset(true);
+    }
+
+    private void reset(
+        boolean clearMac)
+    {
+        S = Arrays.clone(initS);
+        counter = Arrays.clone(J0);
+        bufOff = 0;
+        totalLength = 0;
+
+        if (bufBlock != null)
+        {
+            Arrays.fill(bufBlock, (byte)0);
+        }
+
+        if (clearMac)
+        {
+            macBlock = null;
+        }
+
+        cipher.reset();
+    }
+
+    private void gCTRBlock(byte[] buf, int bufCount, byte[] out, int outOff)
+    {
+//        inc(counter);
+        for (int i = 15; i >= 12; --i)
+        {
+            byte b = (byte)((counter[i] + 1) & 0xff);
+            counter[i] = b;
+
+            if (b != 0)
+            {
+                break;
+            }
+        }
+
+        byte[] tmp = new byte[BLOCK_SIZE];
+        cipher.processBlock(counter, 0, tmp, 0);
+
+        byte[] hashBytes;
+        if (forEncryption)
+        {
+            System.arraycopy(ZEROES, bufCount, tmp, bufCount, BLOCK_SIZE - bufCount);
+            hashBytes = tmp;
+        }
+        else
+        {
+            hashBytes = buf;
+        }
+
+        for (int i = bufCount - 1; i >= 0; --i)
+        {
+            tmp[i] ^= buf[i];
+            out[outOff + i] = tmp[i];
+        }
+
+//        gHASHBlock(hashBytes);
+        xor(S, hashBytes);
+        multiplier.multiplyH(S);
+
+        totalLength += bufCount;
+    }
+
+    private byte[] gHASH(byte[] b)
+    {
+        byte[] Y = new byte[16];
+
+        for (int pos = 0; pos < b.length; pos += 16)
+        {
+            byte[] X = new byte[16];
+            int num = Math.min(b.length - pos, 16);
+            System.arraycopy(b, pos, X, 0, num);
+            xor(Y, X);
+            multiplier.multiplyH(Y);
+        }
+
+        return Y;
+    }
+
+//    private void gHASHBlock(byte[] block)
+//    {
+//        xor(S, block);
+//        multiplier.multiplyH(S);
+//    }
+
+//    private static void inc(byte[] block)
+//    {
+//        for (int i = 15; i >= 12; --i)
+//        {
+//            byte b = (byte)((block[i] + 1) & 0xff);
+//            block[i] = b;
+//
+//            if (b != 0)
+//            {
+//                break;
+//            }
+//        }
+//    }
+
+    private static void xor(byte[] block, byte[] val)
+    {
+        for (int i = 15; i >= 0; --i)
+        {
+            block[i] ^= val[i];
+        }
+    }
+
+    private static void packLength(long count, byte[] bs, int off)
+    {
+        Pack.intToBigEndian((int)(count >>> 32), bs, off); 
+        Pack.intToBigEndian((int)count, bs, off + 4);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
index 007bffc..3886341 100644
--- a/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
@@ -39,7 +39,7 @@
         
         if (blockSize != 8)
         {
-            throw new IllegalArgumentException("GTCR only for 64 bit block ciphers");
+            throw new IllegalArgumentException("GCTR only for 64 bit block ciphers");
         }
 
         this.IV = new byte[cipher.getBlockSize()];
diff --git a/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
deleted file mode 100644
index f15ed67..0000000
--- a/src/main/java/org/bouncycastle/crypto/modes/PaddedBlockCipher.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package org.bouncycastle.crypto.modes;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-
-/**
- * A wrapper class that allows block ciphers to be used to process data in
- * a piecemeal fashion with PKCS5/PKCS7 padding. The PaddedBlockCipher
- * outputs a block only when the buffer is full and more data is being added,
- * or on a doFinal (unless the current block in the buffer is a pad block).
- * The padding mechanism used is the one outlined in PKCS5/PKCS7.
- *
- * @deprecated use org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher instead.
- */
-public class PaddedBlockCipher
-    extends BufferedBlockCipher
-{
-    /**
-     * Create a buffered block cipher with, or without, padding.
-     *
-     * @param cipher the underlying block cipher this buffering object wraps.
-     */
-    public PaddedBlockCipher(
-        BlockCipher     cipher)
-    {
-        this.cipher = cipher;
-
-        buf = new byte[cipher.getBlockSize()];
-        bufOff = 0;
-    }
-
-    /**
-     * return the size of the output buffer required for an update plus a
-     * doFinal with an input of len bytes.
-     *
-     * @param len the length of the input.
-     * @return the space required to accommodate a call to update and doFinal
-     * with len bytes of input.
-     */
-    public int getOutputSize(
-        int len)
-    {
-        int total       = len + bufOff;
-        int leftOver    = total % buf.length;
-
-        if (leftOver == 0)
-        {
-            if (forEncryption)
-            {
-                return total + buf.length;
-            }
-
-            return total;
-        }
-
-        return total - leftOver + buf.length;
-    }
-
-    /**
-     * return the size of the output buffer required for an update 
-     * an input of len bytes.
-     *
-     * @param len the length of the input.
-     * @return the space required to accommodate a call to update
-     * with len bytes of input.
-     */
-    public int getUpdateOutputSize(
-        int len)
-    {
-        int total       = len + bufOff;
-        int leftOver    = total % buf.length;
-
-        if (leftOver == 0)
-        {
-            return total - buf.length;
-        }
-
-        return total - leftOver;
-    }
-
-    /**
-     * process a single byte, producing an output block if neccessary.
-     *
-     * @param in the input byte.
-     * @param out the space for any output that might be produced.
-     * @param outOff the offset from which the output will be copied.
-     * @exception DataLengthException if there isn't enough space in out.
-     * @exception IllegalStateException if the cipher isn't initialised.
-     */
-    public int processByte(
-        byte        in,
-        byte[]      out,
-        int         outOff)
-        throws DataLengthException, IllegalStateException
-    {
-        int         resultLen = 0;
-
-        if (bufOff == buf.length)
-        {
-            resultLen = cipher.processBlock(buf, 0, out, outOff);
-            bufOff = 0;
-        }
-
-        buf[bufOff++] = in;
-
-        return resultLen;
-    }
-
-    /**
-     * process an array of bytes, producing output if necessary.
-     *
-     * @param in the input byte array.
-     * @param inOff the offset at which the input data starts.
-     * @param len the number of bytes to be copied out of the input array.
-     * @param out the space for any output that might be produced.
-     * @param outOff the offset from which the output will be copied.
-     * @exception DataLengthException if there isn't enough space in out.
-     * @exception IllegalStateException if the cipher isn't initialised.
-     */
-    public int processBytes(
-        byte[]      in,
-        int         inOff,
-        int         len,
-        byte[]      out,
-        int         outOff)
-        throws DataLengthException, IllegalStateException
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        int blockSize   = getBlockSize();
-        int length      = getUpdateOutputSize(len);
-        
-        if (length > 0)
-        {
-            if ((outOff + length) > out.length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
-        }
-
-        int resultLen = 0;
-        int gapLen = buf.length - bufOff;
-
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-
-            resultLen += cipher.processBlock(buf, 0, out, outOff);
-
-            bufOff = 0;
-            len -= gapLen;
-            inOff += gapLen;
-
-            while (len > buf.length)
-            {
-                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
-
-                len -= blockSize;
-                inOff += blockSize;
-            }
-        }
-
-        System.arraycopy(in, inOff, buf, bufOff, len);
-
-        bufOff += len;
-
-        return resultLen;
-    }
-
-    /**
-     * Process the last block in the buffer. If the buffer is currently
-     * full and padding needs to be added a call to doFinal will produce
-     * 2 * getBlockSize() bytes.
-     *
-     * @param out the array the block currently being held is copied into.
-     * @param outOff the offset at which the copying starts.
-     * @exception DataLengthException if there is insufficient space in out for
-     * the output or we are decrypting and the input is not block size aligned.
-     * @exception IllegalStateException if the underlying cipher is not
-     * initialised.
-     * @exception InvalidCipherTextException if padding is expected and not found.
-     */
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
-        throws DataLengthException, IllegalStateException, InvalidCipherTextException
-    {
-        int blockSize = cipher.getBlockSize();
-        int resultLen = 0;
-
-        if (forEncryption)
-        {
-            if (bufOff == blockSize)
-            {
-                if ((outOff + 2 * blockSize) > out.length)
-                {
-                    throw new DataLengthException("output buffer too short");
-                }
-
-                resultLen = cipher.processBlock(buf, 0, out, outOff);
-                bufOff = 0;
-            }
-
-            //
-            // add PKCS7 padding
-            //
-            byte code = (byte)(blockSize - bufOff);
-
-            while (bufOff < blockSize)
-            {
-                buf[bufOff] = code;
-                bufOff++;
-            }
-
-            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
-        }
-        else
-        {
-            if (bufOff == blockSize)
-            {
-                resultLen = cipher.processBlock(buf, 0, buf, 0);
-                bufOff = 0;
-            }
-            else
-            {
-                throw new DataLengthException("last block incomplete in decryption");
-            }
-
-            //
-            // remove PKCS7 padding
-            //
-            int count = buf[blockSize - 1] & 0xff;
-
-            if ((count < 0) || (count > blockSize))
-            {
-                throw new InvalidCipherTextException("pad block corrupted");
-            }
-
-            resultLen -= count;
-
-            System.arraycopy(buf, 0, out, outOff, resultLen);
-        }
-
-        reset();
-
-        return resultLen;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
index ce0b865..a92e5a5 100644
--- a/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -59,6 +59,10 @@
           reset();
           cipher.init(true, ivParam.getParameters());
         }
+        else
+        {
+            throw new IllegalArgumentException("SIC mode requires ParametersWithIV");
+        }
     }
 
     public String getAlgorithmName()
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java b/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
new file mode 100644
index 0000000..f52f610
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+public interface GCMMultiplier
+{
+    void init(byte[] H);
+    void multiplyH(byte[] x);
+}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
new file mode 100644
index 0000000..e866b34
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.crypto.util.Pack;
+
+abstract class GCMUtil
+{
+    static int[] asInts(byte[] bs)
+    {
+        int[] us = new int[4];
+        us[0] = Pack.bigEndianToInt(bs, 0);
+        us[1] = Pack.bigEndianToInt(bs, 4);
+        us[2] = Pack.bigEndianToInt(bs, 8);
+        us[3] = Pack.bigEndianToInt(bs, 12);
+        return us;
+    }
+
+    // P is the value with only bit i=1 set
+    static void multiplyP(int[] x)
+    {
+        boolean lsb = (x[3] & 1) != 0;
+        shiftRight(x);
+        if (lsb)
+        {
+            // R = new int[]{ 0xe1000000, 0, 0, 0 };
+//            xor(v, R);
+            x[0] ^= 0xe1000000;
+        }
+    }
+
+    static void multiplyP8(int[] x)
+    {
+        for (int i = 8; i != 0; --i)
+        {
+            multiplyP(x);
+        }
+    }
+
+    static void shiftRight(byte[] block)
+    {
+        int i = 0;
+        int bit = 0;
+        for (;;)
+        {
+            int b = block[i] & 0xff;
+            block[i] = (byte) ((b >>> 1) | bit);
+            if (++i == 16)
+            {
+                break;
+            }
+            bit = (b & 1) << 7;
+        }
+    }
+
+    static void shiftRight(int[] block)
+    {
+        int i = 0;
+        int bit = 0;
+        for (;;)
+        {
+            int b = block[i];
+            block[i] = (b >>> 1) | bit;
+            if (++i == 4)
+            {
+                break;
+            }
+            bit = b << 31;
+        }
+    }
+
+    static void xor(byte[] block, byte[] val)
+    {
+        for (int i = 15; i >= 0; --i)
+        {
+            block[i] ^= val[i];
+        }
+    }
+
+    static void xor(int[] block, int[] val)
+    {
+        for (int i = 3; i >= 0; --i)
+        {
+            block[i] ^= val[i];
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
new file mode 100644
index 0000000..47728b1
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import org.bouncycastle.crypto.util.Pack;
+
+public class Tables8kGCMMultiplier implements GCMMultiplier
+{
+    private final int[][][] M = new int[32][16][];
+
+    public void init(byte[] H)
+    {
+        M[0][0] = new int[4];
+        M[1][0] = new int[4];
+        M[1][8] = GCMUtil.asInts(H);
+
+        for (int j = 4; j >= 1; j >>= 1)
+        {
+            int[] tmp = new int[4];
+            System.arraycopy(M[1][j + j], 0, tmp, 0, 4);
+
+            GCMUtil.multiplyP(tmp);
+            M[1][j] = tmp;
+        }
+
+        {
+            int[] tmp = new int[4];
+            System.arraycopy(M[1][1], 0, tmp, 0, 4);
+
+            GCMUtil.multiplyP(tmp);
+            M[0][8] = tmp;
+        }
+
+        for (int j = 4; j >= 1; j >>= 1)
+        {
+            int[] tmp = new int[4];
+            System.arraycopy(M[0][j + j], 0, tmp, 0, 4);
+
+            GCMUtil.multiplyP(tmp);
+            M[0][j] = tmp;
+        }
+
+        int i = 0;
+        for (;;)
+        {
+            for (int j = 2; j < 16; j += j)
+            {
+                for (int k = 1; k < j; ++k)
+                {
+                    int[] tmp = new int[4];
+                    System.arraycopy(M[i][j], 0, tmp, 0, 4);
+
+                    GCMUtil.xor(tmp, M[i][k]);
+                    M[i][j + k] = tmp;
+                }
+            }
+
+            if (++i == 32)
+            {
+                return;
+            }
+
+            if (i > 1)
+            {
+                M[i][0] = new int[4];
+                for(int j = 8; j > 0; j >>= 1)
+                {
+                  int[] tmp = new int[4];
+                  System.arraycopy(M[i - 2][j], 0, tmp, 0, 4);
+
+                  GCMUtil.multiplyP8(tmp);
+                  M[i][j] = tmp;
+                }
+            }
+        }
+    }
+
+    public void multiplyH(byte[] x)
+    {
+//      assert x.Length == 16;
+
+        int[] z = new int[4];
+        for (int i = 15; i >= 0; --i)
+        {
+//            GCMUtil.xor(z, M[i + i][x[i] & 0x0f]);
+            int[] m = M[i + i][x[i] & 0x0f];
+            z[0] ^= m[0];
+            z[1] ^= m[1];
+            z[2] ^= m[2];
+            z[3] ^= m[3];
+//            GCMUtil.xor(z, M[i + i + 1][(x[i] & 0xf0) >>> 4]);
+            m = M[i + i + 1][(x[i] & 0xf0) >>> 4];
+            z[0] ^= m[0];
+            z[1] ^= m[1];
+            z[2] ^= m[2];
+            z[3] ^= m[3];
+        }
+
+        Pack.intToBigEndian(z[0], x, 0);
+        Pack.intToBigEndian(z[1], x, 4);
+        Pack.intToBigEndian(z[2], x, 8);
+        Pack.intToBigEndian(z[3], x, 12);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
index c005ff8..93b149f 100644
--- a/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
+++ b/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -58,7 +58,7 @@
     {
         int count = in[in.length - 1] & 0xff;
 
-        if (count > in.length)
+        if (count > in.length || count == 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java b/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
similarity index 85%
rename from src/main/java/org/bouncycastle/crypto/params/CCMParameters.java
rename to src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
index 17f7344..b60ef40 100644
--- a/src/main/java/org/bouncycastle/crypto/params/CCMParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
@@ -2,7 +2,7 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 
-public class CCMParameters
+public class AEADParameters
     implements CipherParameters
 {
     private byte[] associatedText;
@@ -12,35 +12,35 @@
 
     /**
      * Base constructor.
-     * 
+     *
      * @param key key to be used by underlying cipher
      * @param macSize macSize in bits
      * @param nonce nonce to be used
      * @param associatedText associated text, if any
      */
-    public CCMParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
+    public AEADParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
     {
         this.key = key;
         this.nonce = nonce;
         this.macSize = macSize;
         this.associatedText = associatedText;
     }
-    
+
     public KeyParameter getKey()
     {
         return key;
     }
-    
+
     public int getMacSize()
     {
         return macSize;
     }
-    
+
     public byte[] getAssociatedText()
     {
         return associatedText;
     }
-    
+
     public byte[] getNonce()
     {
         return nonce;
diff --git a/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java b/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
index d424fc1..3a4bbfc 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
@@ -13,7 +13,7 @@
     {
         super(key);
 
-        if (isWeakKey(key, 0, 0))
+        if (isWeakKey(key, 0, key.length))
         {
             throw new IllegalArgumentException("attempt to create weak DESede key");
         }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java b/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
index 910081e..34c730e 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
@@ -13,7 +13,7 @@
         SecureRandom    random,
         DHParameters    params)
     {
-        super(random, params.getP().bitLength());
+        super(random, getStrength(params));
 
         this.params = params;
     }
@@ -22,4 +22,9 @@
     {
         return params;
     }
+
+    static int getStrength(DHParameters params)
+    {
+        return params.getL() != 0 ? params.getL() : params.getP().bitLength();
+    }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHParameters.java b/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
index def449c..95352ce 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
@@ -1,50 +1,107 @@
 package org.bouncycastle.crypto.params;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.crypto.CipherParameters;
 
+import java.math.BigInteger;
+
 public class DHParameters
     implements CipherParameters
 {
+    private static final int DEFAULT_MINIMUM_LENGTH = 160;
+
+    // not final due to compiler bug in "simpler" JDKs
     private BigInteger              g;
     private BigInteger              p;
     private BigInteger              q;
-    private int                     j;
+    private BigInteger              j;
+    private int                     m;
+    private int                     l;
     private DHValidationParameters  validation;
 
+    private static int getDefaultMParam(
+        int lParam)
+    {
+        if (lParam == 0)
+        {
+            return DEFAULT_MINIMUM_LENGTH;
+        }
+
+        return lParam < DEFAULT_MINIMUM_LENGTH ? lParam : DEFAULT_MINIMUM_LENGTH;
+    }
+
     public DHParameters(
         BigInteger  p,
         BigInteger  g)
     {
-        this.g = g;
-        this.p = p;
+        this(p, g, null, 0);
+    }
+
+    public DHParameters(
+        BigInteger  p,
+        BigInteger  g,
+        BigInteger  q)
+    {
+        this(p, g, q, 0);
     }
 
     public DHParameters(
         BigInteger  p,
         BigInteger  g,
         BigInteger  q,
-        int         j)
+        int         l)
     {
-        this.g = g;
-        this.p = p;
-        this.q = q;
-        this.j = j;
-    }   
+        this(p, g, q, getDefaultMParam(l), l, null, null);
+    }
+
+    public DHParameters(
+        BigInteger  p,
+        BigInteger  g,
+        BigInteger  q,
+        int         m,
+        int         l)
+    {
+        this(p, g, q, m, l, null, null);
+    }
 
     public DHParameters(
         BigInteger              p,
         BigInteger              g,
         BigInteger              q,
-        int                     j,
+        BigInteger              j,
         DHValidationParameters  validation)
     {
+        this(p, g, q, DEFAULT_MINIMUM_LENGTH, 0, j, validation);
+    }
+
+    public DHParameters(
+        BigInteger              p,
+        BigInteger              g,
+        BigInteger              q,
+        int                     m,
+        int                     l,
+        BigInteger              j,
+        DHValidationParameters  validation)
+    {
+        if (l != 0)
+        {
+            if (l >= p.bitLength())
+            {
+                throw new IllegalArgumentException("when l value specified, it must be less than bitlength(p)");
+            }
+            if (l < m)
+            {
+                throw new IllegalArgumentException("when l value specified, it may not be less than m value");
+            }
+        }
+
         this.g = g;
         this.p = p;
         this.q = q;
+        this.m = m;
+        this.l = l;
         this.j = j;
-    }   
+        this.validation = validation;
+    }
 
     public BigInteger getP()
     {
@@ -62,15 +119,35 @@
     }
 
     /**
-     * Return the private value length in bits - if set, zero otherwise (use bitLength(P) - 1).
-     * 
-     * @return the private value length in bits, zero otherwise.
+     * Return the subgroup factor J.
+     *
+     * @return subgroup factor
      */
-    public int getJ()
+    public BigInteger getJ()
     {
         return j;
     }
 
+    /**
+     * Return the minimum length of the private value.
+     *
+     * @return the minimum length of the private value in bits.
+     */
+    public int getM()
+    {
+        return m;
+    }
+
+    /**
+     * Return the private value length in bits - if set, zero otherwise
+     *
+     * @return the private value length in bits, zero otherwise.
+     */
+    public int getL()
+    {
+        return l;
+    }
+
     public DHValidationParameters getValidationParameters()
     {
         return validation;
@@ -86,21 +163,6 @@
 
         DHParameters    pm = (DHParameters)obj;
 
-        if (this.getValidationParameters() != null)
-        {
-            if (!this.getValidationParameters().equals(pm.getValidationParameters()))
-            {
-                return false;
-            }
-        }
-        else
-        {
-            if (pm.getValidationParameters() != null)
-            {
-                return false;
-            }
-        }
-
         if (this.getQ() != null)
         {
             if (!this.getQ().equals(pm.getQ()))
@@ -115,12 +177,12 @@
                 return false;
             }
         }
-        
-        return (j == pm.getJ()) && pm.getP().equals(p) && pm.getG().equals(g);
+
+        return pm.getP().equals(p) && pm.getG().equals(g);
     }
     
     public int hashCode()
     {
-        return getJ() ^ getP().hashCode() ^ getG().hashCode();
+        return getP().hashCode() ^ getG().hashCode() ^ (getQ() != null ? getQ().hashCode() : 0);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java b/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
index 523f9a0..ee1b34f 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
@@ -21,6 +21,11 @@
         return x;
     }
 
+    public int hashCode()
+    {
+        return x.hashCode() ^ super.hashCode();
+    }
+    
     public boolean equals(
         Object  obj)
     {
@@ -29,13 +34,8 @@
             return false;
         }
 
-        DHPrivateKeyParameters  pKey = (DHPrivateKeyParameters)obj;
+        DHPrivateKeyParameters  other = (DHPrivateKeyParameters)obj;
 
-        if (!pKey.getX().equals(x))
-        {
-            return false;
-        }
-
-        return super.equals(obj);
+        return other.getX().equals(this.x) && super.equals(obj);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java b/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
index 4f308ed..ed53160 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
@@ -21,6 +21,11 @@
         return y;
     }
 
+    public int hashCode()
+    {
+        return y.hashCode() ^ super.hashCode();
+    }
+
     public boolean equals(
         Object  obj)
     {
@@ -29,13 +34,8 @@
             return false;
         }
 
-        DHPublicKeyParameters   pKey = (DHPublicKeyParameters)obj;
+        DHPublicKeyParameters   other = (DHPublicKeyParameters)obj;
 
-        if (!pKey.getY().equals(y))
-        {
-            return false;
-        }
-
-        return super.equals(obj);
+        return other.getY().equals(y) && super.equals(obj);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java b/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
index 94c1360..b22f7a0 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.params;
 
+import org.bouncycastle.util.Arrays;
+
 public class DHValidationParameters
 {
     private byte[]  seed;
@@ -13,6 +15,16 @@
         this.counter = counter;
     }
 
+    public int getCounter()
+    {
+        return counter;
+    }
+
+    public byte[] getSeed()
+    {
+        return seed;
+    }
+
     public boolean equals(
         Object o)
     {
@@ -28,31 +40,11 @@
             return false;
         }
 
-        if (other.seed.length != this.seed.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != other.seed.length; i++)
-        {
-            if (other.seed[i] != this.seed[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return Arrays.areEqual(this.seed, other.seed);
     }
-    
+
     public int hashCode()
     {
-        int code = counter;
-        
-        for (int i = 0; i != seed.length; i++)
-        {
-            code ^= (seed[i] & 0xff) << (i % 4);
-        }
-        
-        return code;
+        return counter ^ Arrays.hashCode(seed);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java b/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
index 541218b..1cc4b93 100644
--- a/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
+++ b/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.params;
 
+import org.bouncycastle.util.Arrays;
+
 public class DSAValidationParameters
 {
     private byte[]  seed;
@@ -23,6 +25,11 @@
         return seed;
     }
 
+    public int hashCode()
+    {
+        return counter ^ Arrays.hashCode(seed);
+    }
+    
     public boolean equals(
         Object o)
     {
@@ -38,19 +45,6 @@
             return false;
         }
 
-        if (other.seed.length != this.seed.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != other.seed.length; i++)
-        {
-            if (other.seed[i] != this.seed[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
+        return Arrays.areEqual(this.seed, other.seed);
     }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/params/IESParameters.java b/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
deleted file mode 100644
index 0600b34..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/IESParameters.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.CipherParameters;
-
-/**
- * parameters for using an integrated cipher in stream mode.
- */
-public class IESParameters
-    implements CipherParameters
-{
-    private byte[]  derivation;
-    private byte[]  encoding;
-    private int     macKeySize;
-
-    /**
-     * @param derivation the derivation parameter for the KDF function.
-     * @param encoding the encoding parameter for the KDF function.
-     * @param macKeySize the size of the MAC key (in bits).
-     */
-    public IESParameters(
-        byte[]  derivation,
-        byte[]  encoding,
-        int     macKeySize)
-    {
-        this.derivation = derivation;
-        this.encoding = encoding;
-        this.macKeySize = macKeySize;
-    }
-
-    public byte[] getDerivationV()
-    {
-        return derivation;
-    }
-
-    public byte[] getEncodingV()
-    {
-        return encoding;
-    }
-
-    public int getMacKeySize()
-    {
-        return macKeySize;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java b/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java
deleted file mode 100644
index ef61b2c..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/IESWithCipherParameters.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-
-public class IESWithCipherParameters
-    extends IESParameters
-{
-    private int cipherKeySize;
-
-    /**
-     * @param derivation the derivation parameter for the KDF function.
-     * @param encoding the encoding parameter for the KDF function.
-     * @param macKeySize the size of the MAC key (in bits).
-     * @param cipherKeySize the size of the associated Cipher key (in bits).
-     */
-    public IESWithCipherParameters(
-        byte[]  derivation,
-        byte[]  encoding,
-        int     macKeySize,
-        int     cipherKeySize)
-    {
-        super(derivation, encoding, macKeySize);
-
-        this.cipherKeySize = cipherKeySize;
-    }
-
-    public int getCipherKeySize()
-    {
-        return cipherKeySize;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java b/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java
deleted file mode 100644
index 8dffe2e..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/ISO18033KDFParameters.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.DerivationParameters;
-
-/**
- * parameters for Key derivation functions for ISO-18033
- */
-public class ISO18033KDFParameters
-    implements DerivationParameters
-{
-    byte[]  seed;
-
-    public ISO18033KDFParameters(
-        byte[]  seed)
-    {
-        this.seed = seed;
-    }
-
-    public byte[] getSeed()
-    {
-        return seed;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java b/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java
deleted file mode 100644
index f3bac64..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/KDFParameters.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.DerivationParameters;
-
-/**
- * parameters for Key derivation functions for IEEE P1363a
- */
-public class KDFParameters
-    implements DerivationParameters
-{
-    byte[]  iv;
-    byte[]  shared;
-
-    public KDFParameters(
-        byte[]  shared,
-        byte[]  iv)
-    {
-        this.shared = shared;
-        this.iv = iv;
-    }
-
-    public byte[] getSharedSecret()
-    {
-        return shared;
-    }
-
-    public byte[] getIV()
-    {
-        return iv;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java b/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java
deleted file mode 100644
index 8c1ea5e..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/MGFParameters.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.DerivationParameters;
-
-/**
- * parameters for mask derivation functions.
- */
-public class MGFParameters
-    implements DerivationParameters
-{
-    byte[]  seed;
-
-    public MGFParameters(
-        byte[]  seed)
-    {
-        this.seed = seed;
-    }
-
-    public MGFParameters(
-        byte[]  seed,
-        int     off,
-        int     len)
-    {
-        this.seed = new byte[len];
-        System.arraycopy(seed, off, this.seed, 0, len);
-    }
-
-    public byte[] getSeed()
-    {
-        return seed;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java b/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
index f8b7c82..a7b18e5 100644
--- a/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
+++ b/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
@@ -1,9 +1,9 @@
 package org.bouncycastle.crypto.params;
 
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.CipherParameters;
 
+import java.security.SecureRandom;
+
 public class ParametersWithRandom
     implements CipherParameters
 {
@@ -21,16 +21,11 @@
     public ParametersWithRandom(
         CipherParameters    parameters)
     {
-        this.random = null;
-        this.parameters = parameters;
+        this(parameters, new SecureRandom());
     }
 
     public SecureRandom getRandom()
     {
-        if (random == null)
-        {
-            random = new SecureRandom();
-        }
         return random;
     }
 
diff --git a/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java b/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java
deleted file mode 100644
index b226a9d..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/ParametersWithSBox.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.CipherParameters;
-
-public class ParametersWithSBox
-    implements CipherParameters
-{
-    private CipherParameters  parameters;
-    private byte[]            sBox;
-
-    public ParametersWithSBox(
-        CipherParameters parameters,
-        byte[]           sBox)
-    {
-        this.parameters = parameters;
-        this.sBox = sBox;
-    }
-
-    public byte[] getSBox()
-    {
-        return sBox;
-    }
-
-    public CipherParameters getParameters()
-    {
-        return parameters;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java b/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java
deleted file mode 100644
index 73765dd..0000000
--- a/src/main/java/org/bouncycastle/crypto/params/ParametersWithSalt.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.bouncycastle.crypto.params;
-
-import org.bouncycastle.crypto.CipherParameters;
-
-/**
- * Cipher parameters with a fixed salt value associated with them.
- */
-public class ParametersWithSalt
-    implements CipherParameters
-{
-    private byte[]              salt;
-    private CipherParameters    parameters;
-
-    public ParametersWithSalt(
-        CipherParameters    parameters,
-        byte[]              salt)
-    {
-        this(parameters, salt, 0, salt.length);
-    }
-
-    public ParametersWithSalt(
-        CipherParameters    parameters,
-        byte[]              salt,
-        int                 saltOff,
-        int                 saltLen)
-    {
-        this.salt = new byte[saltLen];
-        this.parameters = parameters;
-
-        System.arraycopy(salt, saltOff, this.salt, 0, saltLen);
-    }
-
-    public byte[] getSalt()
-    {
-        return salt;
-    }
-
-    public CipherParameters getParameters()
-    {
-        return parameters;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
index a8392ab..a96cef0 100644
--- a/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.crypto.signers;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DSA;
 import org.bouncycastle.crypto.params.DSAKeyParameters;
@@ -11,6 +8,9 @@
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 /**
  * The Digital Signature Algorithm - as described in "Handbook of Applied
  * Cryptography", pages 452 - 453.
@@ -57,8 +57,8 @@
     public BigInteger[] generateSignature(
         byte[] message)
     {
-        BigInteger      m = new BigInteger(1, message);
         DSAParameters   params = key.getParameters();
+        BigInteger      m = calculateE(params.getQ(), message);
         BigInteger      k;
         int                  qBitLength = params.getQ().bitLength();
 
@@ -93,8 +93,8 @@
         BigInteger  r,
         BigInteger  s)
     {
-        BigInteger      m = new BigInteger(1, message);
         DSAParameters   params = key.getParameters();
+        BigInteger      m = calculateE(params.getQ(), message);
         BigInteger      zero = BigInteger.valueOf(0);
 
         if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
@@ -119,4 +119,20 @@
 
         return v.equals(r);
     }
+
+    private BigInteger calculateE(BigInteger n, byte[] message)
+    {
+        if (n.bitLength() >= message.length * 8)
+        {
+            return new BigInteger(1, message);
+        }
+        else
+        {
+            byte[] trunc = new byte[n.bitLength() / 8];
+
+            System.arraycopy(message, 0, trunc, 0, trunc.length);
+
+            return new BigInteger(1, trunc);
+        }
+    }
 }
diff --git a/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java b/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
deleted file mode 100644
index b617758..0000000
--- a/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
+++ /dev/null
@@ -1,611 +0,0 @@
-package org.bouncycastle.crypto.signers;
-
-import java.security.SecureRandom;
-
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.CryptoException;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.SignerWithRecovery;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
-// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-// END android-removed
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.params.ParametersWithSalt;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-
-/**
- * ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
- * <p>
- * Note: the usual length for the salt is the length of the hash 
- * function used in bytes.
- */
-public class ISO9796d2PSSSigner
-    implements SignerWithRecovery
-{
-    static final public int   TRAILER_IMPLICIT    = 0xBC;
-    static final public int   TRAILER_RIPEMD160   = 0x31CC;
-    static final public int   TRAILER_RIPEMD128   = 0x32CC;
-    static final public int   TRAILER_SHA1        = 0x33CC;
-
-    private Digest                      digest;
-    private AsymmetricBlockCipher       cipher;
-    
-    private SecureRandom                random;
-    private byte[]                      standardSalt;
-
-    private int         hLen;
-    private int         trailer;
-    private int         keyBits;
-    private byte[]      block;
-    private byte[]      mBuf;
-    private int         messageLength;
-    private int         saltLength;
-    private boolean     fullMessage;
-    private byte[]      recoveredMessage;
-
-    /**
-     * Generate a signer for the with either implicit or explicit trailers
-     * for ISO9796-2, scheme 2 or 3.
-     * 
-     * @param cipher base cipher to use for signature creation/verification
-     * @param digest digest to use.
-     * @param saltLength length of salt in bytes.
-     * @param implicit whether or not the trailer is implicit or gives the hash.
-     */
-    public ISO9796d2PSSSigner(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest,
-        int                     saltLength,
-        boolean                 implicit)
-    {
-        this.cipher = cipher;
-        this.digest = digest;
-        this.hLen = digest.getDigestSize();
-        this.saltLength = saltLength;
-
-        if (implicit)
-        {
-            trailer = TRAILER_IMPLICIT;
-        }
-        else
-        {
-            if (digest instanceof SHA1Digest)
-            {
-                trailer = TRAILER_SHA1;
-            }
-            // BEGIN android-removed
-            // else if (digest instanceof RIPEMD160Digest)
-            // {
-            //     trailer = TRAILER_RIPEMD160;
-            // }
-            // else if (digest instanceof RIPEMD128Digest)
-            // {
-            //     trailer = TRAILER_RIPEMD128;
-            // }
-            // END android-removed
-            else
-            {
-                throw new IllegalArgumentException("no valid trailer for digest");
-            }
-        }
-    }
-
-    /**
-     * Constructor for a signer with an explicit digest trailer.
-     * 
-     * @param cipher cipher to use.
-     * @param digest digest to sign with.
-     * @param saltLength length of salt in bytes.
-     */
-    public ISO9796d2PSSSigner(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest,
-        int                     saltLength)
-    {
-        this(cipher, digest, saltLength, false);
-    }
-    
-    /**
-     * Initialise the signer.
-     * 
-     * @param forSigning true if for signing, false if for verification.
-     * @param param parameters for signature generation/verification. If the
-     * parameters are for generation they should be a ParametersWithRandom,
-     * a ParametersWithSalt, or just an RSAKeyParameters object. If RSAKeyParameters
-     * are passed in a SecureRandom will be created.
-     * @exception IllegalArgumentException if wrong parameter type or a fixed 
-     * salt is passed in which is the wrong length.
-     */
-    public void init(
-        boolean                 forSigning,
-        CipherParameters        param)
-    {
-        RSAKeyParameters    kParam = null;
-        int                    lengthOfSalt = saltLength;
-
-        if (param instanceof ParametersWithRandom)
-        {
-            ParametersWithRandom    p = (ParametersWithRandom)param;
-
-            kParam = (RSAKeyParameters)p.getParameters();
-            random = p.getRandom();
-        }
-        else if (param instanceof ParametersWithSalt)
-        {
-            ParametersWithSalt    p = (ParametersWithSalt)param;
-
-            kParam = (RSAKeyParameters)p.getParameters();
-            standardSalt = p.getSalt();
-            lengthOfSalt = standardSalt.length;
-        }
-        else
-        {
-            kParam = (RSAKeyParameters)param;
-            if (forSigning)
-            {
-                random = new SecureRandom();
-            }
-        }
-        
-        cipher.init(forSigning, kParam);
-
-        keyBits = kParam.getModulus().bitLength();
-
-        block = new byte[(keyBits + 7) / 8];
-        
-        if (trailer == TRAILER_IMPLICIT)
-        {
-            mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 1];
-        }
-        else
-        {
-            mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 2];
-        }
-
-        reset();
-    }
-
-    /**
-     * compare two byte arrays.
-     */
-    private boolean isSameAs(
-        byte[]    a,
-        byte[]    b)
-    {
-        if (messageLength != b.length)
-        {
-            return false;
-        }
-        
-        for (int i = 0; i != b.length; i++)
-        {
-            if (a[i] != b[i])
-            {
-                return false;
-            }
-        }
-        
-        return true;
-    }
-    
-    /**
-     * clear possible sensitive data
-     */
-    private void clearBlock(
-        byte[]  block)
-    {
-        for (int i = 0; i != block.length; i++)
-        {
-            block[i] = 0;
-        }
-    }
-
-    /**
-     * update the internal digest with the byte b
-     */
-    public void update(
-        byte    b)
-    {
-        if (messageLength < mBuf.length)
-        {
-            mBuf[messageLength++] = b;
-        }
-        else
-        {
-            digest.update(b);
-        }
-    }
-
-    /**
-     * update the internal digest with the byte array in
-     */
-    public void update(
-        byte[]  in,
-        int     off,
-        int     len)
-    {
-        while (len > 0 && messageLength < mBuf.length)
-        {
-            this.update(in[off]);
-            off++;
-            len--;
-        }
-        
-        if (len > 0)
-        {
-            digest.update(in, off, len);
-        }
-    }
-
-    /**
-     * reset the internal state
-     */
-    public void reset()
-    {
-        digest.reset();
-        messageLength = 0;
-        if (recoveredMessage != null)
-        {
-            clearBlock(recoveredMessage);
-        }
-        recoveredMessage = null;
-        fullMessage = false;
-    }
-
-    /**
-     * generate a signature for the loaded message using the key we were
-     * initialised with.
-     */
-    public byte[] generateSignature()
-        throws CryptoException
-    {
-        int     digSize = digest.getDigestSize();
-
-        byte[]    m2Hash = new byte[digSize];
-  
-        digest.doFinal(m2Hash, 0);
-
-        byte[]  C = new byte[8];
-        LtoOSP(messageLength * 8, C);
-
-        digest.update(C, 0, C.length);
-
-        digest.update(mBuf, 0, messageLength);
-        
-        digest.update(m2Hash, 0, m2Hash.length);
-        
-        byte[]    salt;
-        
-        if (standardSalt != null)
-        {
-            salt = standardSalt;
-        }
-        else
-        {
-            salt = new byte[saltLength];
-            random.nextBytes(salt);
-        }
-        
-        digest.update(salt, 0, salt.length);
-
-        byte[]    hash = new byte[digest.getDigestSize()];
-        
-        digest.doFinal(hash, 0);
-        
-        int tLength = 2;
-        if (trailer == TRAILER_IMPLICIT)
-        {
-            tLength = 1;
-        }
-        
-        int    off = block.length - messageLength - salt.length - hLen - tLength - 1;
-        
-        block[off] = 0x01;
-  
-        System.arraycopy(mBuf, 0, block, off + 1, messageLength);
-        System.arraycopy(salt, 0, block, off + 1 + messageLength, salt.length);
-
-        byte[] dbMask = maskGeneratorFunction1(hash, 0, hash.length, block.length - hLen - tLength);
-        for (int i = 0; i != dbMask.length; i++)
-        {
-            block[i] ^= dbMask[i];
-        }
-        
-        System.arraycopy(hash, 0, block, block.length - hLen - tLength, hLen);
-        
-        if (trailer == TRAILER_IMPLICIT)
-        {
-            block[block.length - 1] = (byte)TRAILER_IMPLICIT;
-        }
-        else
-        {
-            block[block.length - 2] = (byte)(trailer >>> 8);
-            block[block.length - 1] = (byte)trailer;
-        }
-        
-        block[0] &= 0x7f;
-
-        byte[]  b = cipher.processBlock(block, 0, block.length);
-
-        clearBlock(mBuf);
-        clearBlock(block);
-        messageLength = 0;
-
-        return b;
-    }
-
-    /**
-     * return true if the signature represents a ISO9796-2 signature
-     * for the passed in message.
-     */
-    public boolean verifySignature(
-        byte[]      signature)
-    {
-        byte[]      block = null;
-
-        try
-        {
-            block = cipher.processBlock(signature, 0, signature.length);
-        }
-        catch (Exception e)
-        {
-            return false;
-        }
-        
-        //
-        // adjust block size for leading zeroes if necessary
-        //
-        if (block.length < (keyBits + 7) / 8)
-        {
-            byte[] tmp = new byte[(keyBits + 7) / 8];
-
-            System.arraycopy(block, 0, tmp, tmp.length - block.length, block.length);
-            block = tmp;
-        }
-
-        int     tLength = 0;
-
-        if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
-        {
-            tLength = 1;
-        }
-        else
-        {
-            int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
-
-            switch (sigTrail)
-            {
-            // BEGIN android-removed
-            // case TRAILER_RIPEMD160:
-            //         if (!(digest instanceof RIPEMD160Digest))
-            //         {
-            //             throw new IllegalStateException("signer should be initialised with RIPEMD160");
-            //         }
-            //         break;
-            // END android-removed
-            case TRAILER_SHA1:
-                    if (!(digest instanceof SHA1Digest))
-                    {
-                        throw new IllegalStateException("signer should be initialised with SHA1");
-                    }
-                    break;
-            // BEGIN android-removed
-            // case TRAILER_RIPEMD128:
-            //         if (!(digest instanceof RIPEMD128Digest))
-            //         {
-            //             throw new IllegalStateException("signer should be initialised with RIPEMD128");
-            //         }
-            //        break;
-            // END android-removed
-            default:
-                throw new IllegalArgumentException("unrecognised hash in signature");
-            }
-
-            tLength = 2;
-        }
-
-        //
-        // calculate H(m2)
-        //
-        byte[]    m2Hash = new byte[hLen];
-        digest.doFinal(m2Hash, 0);
-        
-        //
-        // remove the mask
-        //
-        byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - tLength, hLen, block.length - hLen - tLength);
-        for (int i = 0; i != dbMask.length; i++)
-        {
-            block[i] ^= dbMask[i];
-        }
-
-        block[0] &= 0x7f;
-        
-        //
-        // find out how much padding we've got
-        //
-        int mStart = 0;
-
-        for (mStart = 0; mStart != block.length; mStart++)
-        {
-            if (block[mStart] == 0x01)
-            {
-                break;
-            }
-        }
-
-        mStart++;
-
-        if (mStart >= block.length)
-        {
-            clearBlock(block);
-            return false;
-        }
-        
-        if (mStart > 1)
-        {
-            fullMessage = true;
-        }
-        else
-        {
-            fullMessage = false;
-        }
-        
-        recoveredMessage = new byte[dbMask.length - mStart - saltLength];
-
-        System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
-
-        //
-        // check the hashes
-        //
-        byte[]  C = new byte[8];
-        LtoOSP(recoveredMessage.length * 8, C);
-        
-        digest.update(C, 0, C.length);
-
-        if (recoveredMessage.length != 0)
-        {
-            digest.update(recoveredMessage, 0, recoveredMessage.length);
-        }
-
-        digest.update(m2Hash, 0, m2Hash.length);
-        byte[]  hash = new byte[digest.getDigestSize()];
-
-        digest.update(block, mStart + recoveredMessage.length, dbMask.length - mStart - recoveredMessage.length);
-        
-        digest.doFinal(hash, 0);
-
-        int off = block.length - tLength - hash.length;
-        
-        for (int i = 0; i != hash.length; i++)
-        {
-            if (hash[i] != block[off + i])
-            {
-                clearBlock(block);
-                clearBlock(hash);
-                clearBlock(recoveredMessage);
-                fullMessage = false;
-                
-                return false;
-            }
-        }
-
-        //
-        // if they've input a message check what we've recovered against
-        // what was input.
-        //
-        if (messageLength != 0)
-        {
-            if (!isSameAs(mBuf, recoveredMessage))
-            {
-                clearBlock(mBuf);
-                clearBlock(block);
-               
-                return false;
-            }
-        }
-        
-        clearBlock(mBuf);
-        clearBlock(block);
-        messageLength = 0;
-
-        return true;
-    }
-
-    /**
-     * Return true if the full message was recoveredMessage.
-     * 
-     * @return true on full message recovery, false otherwise, or if not sure.
-     * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
-     */
-    public boolean hasFullMessage()
-    {
-        return fullMessage;
-    }
-
-    /**
-     * Return a reference to the recoveredMessage message.
-     * 
-     * @return the full/partial recoveredMessage message.
-     * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
-     */
-    public byte[] getRecoveredMessage()
-    {
-        return recoveredMessage;
-    }
-    
-    /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
-     * long to octet string.
-     */
-    private void LtoOSP(
-        long    l,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(l >>> 56);
-        sp[1] = (byte)(l >>> 48);
-        sp[2] = (byte)(l >>> 40);
-        sp[3] = (byte)(l >>> 32);
-        sp[4] = (byte)(l >>> 24);
-        sp[5] = (byte)(l >>> 16);
-        sp[6] = (byte)(l >>> 8);
-        sp[7] = (byte)(l >>> 0);
-    }
-    /**
-     * mask generator function, as described in PKCS1v2.
-     */
-    private byte[] maskGeneratorFunction1(
-        byte[]  Z,
-        int     zOff,
-        int     zLen,
-        int     length)
-    {
-        byte[]  mask = new byte[length];
-        byte[]  hashBuf = new byte[hLen];
-        byte[]  C = new byte[4];
-        int     counter = 0;
-
-        digest.reset();
-
-        while (counter < (length / hLen))
-        {
-            ItoOSP(counter, C);
-
-            digest.update(Z, zOff, zLen);
-            digest.update(C, 0, C.length);
-            digest.doFinal(hashBuf, 0);
-
-            System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
-            
-            counter++;
-        }
-
-        if ((counter * hLen) < length)
-        {
-            ItoOSP(counter, C);
-
-            digest.update(Z, zOff, zLen);
-            digest.update(C, 0, C.length);
-            digest.doFinal(hashBuf, 0);
-
-            System.arraycopy(hashBuf, 0, mask, counter * hLen, mask.length - (counter * hLen));
-        }
-
-        return mask;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java b/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
deleted file mode 100644
index 43e3017..0000000
--- a/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2Signer.java
+++ /dev/null
@@ -1,495 +0,0 @@
-package org.bouncycastle.crypto.signers;
-
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.CryptoException;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.SignerWithRecovery;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
-// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-// END android-removed
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-
-/**
- * ISO9796-2 - mechanism using a hash function with recovery (scheme 1)
- */
-public class ISO9796d2Signer
-    implements SignerWithRecovery
-{
-    static final public int   TRAILER_IMPLICIT    = 0xBC;
-    static final public int   TRAILER_RIPEMD160   = 0x31CC;
-    static final public int   TRAILER_RIPEMD128   = 0x32CC;
-    static final public int   TRAILER_SHA1        = 0x33CC;
-
-    private Digest                      digest;
-    private AsymmetricBlockCipher       cipher;
-
-    private int         trailer;
-    private int         keyBits;
-    private byte[]      block;
-    private byte[]      mBuf;
-    private int         messageLength;
-    private boolean     fullMessage;
-    private byte[]      recoveredMessage;
-
-    /**
-     * Generate a signer for the with either implicit or explicit trailers
-     * for ISO9796-2.
-     * 
-     * @param cipher base cipher to use for signature creation/verification
-     * @param digest digest to use.
-     * @param implicit whether or not the trailer is implicit or gives the hash.
-     */
-    public ISO9796d2Signer(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest,
-        boolean                 implicit)
-    {
-        this.cipher = cipher;
-        this.digest = digest;
-
-        if (implicit)
-        {
-            trailer = TRAILER_IMPLICIT;
-        }
-        else
-        {
-            if (digest instanceof SHA1Digest)
-            {
-                trailer = TRAILER_SHA1;
-            }
-            // BEGIN android-removed
-            // else if (digest instanceof RIPEMD160Digest)
-            // {
-            //     trailer = TRAILER_RIPEMD160;
-            // }
-            // else if (digest instanceof RIPEMD128Digest)
-            // {
-            //     trailer = TRAILER_RIPEMD128;
-            // }
-            // END android-removed
-            else
-            {
-                throw new IllegalArgumentException("no valid trailer for digest");
-            }
-        }
-    }
-
-    /**
-     * Constructor for a signer with an explicit digest trailer.
-     * 
-     * @param cipher cipher to use.
-     * @param digest digest to sign with.
-     */
-    public ISO9796d2Signer(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest)
-    {
-        this(cipher, digest, false);
-    }
-    
-    public void init(
-        boolean                 forSigning,
-        CipherParameters        param)
-    {
-        RSAKeyParameters  kParam = (RSAKeyParameters)param;
-
-        cipher.init(forSigning, kParam);
-
-        keyBits = kParam.getModulus().bitLength();
-
-        block = new byte[(keyBits + 7) / 8];
-        
-        if (trailer == TRAILER_IMPLICIT)
-        {
-            mBuf = new byte[block.length - digest.getDigestSize() - 2];
-        }
-        else
-        {
-            mBuf = new byte[block.length - digest.getDigestSize() - 3];
-        }
-
-        reset();
-    }
-
-    /**
-     * compare two byte arrays.
-     */
-    private boolean isSameAs(
-        byte[]    a,
-        byte[]    b)
-    {
-        if (messageLength > mBuf.length)
-        {
-            if (mBuf.length > b.length)
-            {
-                return false;
-            }
-            
-            for (int i = 0; i != mBuf.length; i++)
-            {
-                if (a[i] != b[i])
-                {
-                    return false;
-                }
-            }
-        }
-        else
-        {
-            if (messageLength != b.length)
-            {
-                return false;
-            }
-            
-            for (int i = 0; i != b.length; i++)
-            {
-                if (a[i] != b[i])
-                {
-                    return false;
-                }
-            }
-        }
-        
-        return true;
-    }
-    
-    /**
-     * clear possible sensitive data
-     */
-    private void clearBlock(
-        byte[]  block)
-    {
-        for (int i = 0; i != block.length; i++)
-        {
-            block[i] = 0;
-        }
-    }
-
-    /**
-     * update the internal digest with the byte b
-     */
-    public void update(
-        byte    b)
-    {
-        digest.update(b);
-
-        if (messageLength < mBuf.length)
-        {
-            mBuf[messageLength] = b;
-        }
-
-        messageLength++;
-    }
-
-    /**
-     * update the internal digest with the byte array in
-     */
-    public void update(
-        byte[]  in,
-        int     off,
-        int     len)
-    {
-        digest.update(in, off, len);
-
-        if (messageLength < mBuf.length)
-        {
-            for (int i = 0; i < len && (i + messageLength) < mBuf.length; i++)
-            {
-                mBuf[messageLength + i] = in[off + i];
-            }
-        }
-
-        messageLength += len;
-    }
-
-    /**
-     * reset the internal state
-     */
-    public void reset()
-    {
-        digest.reset();
-        messageLength = 0;
-        clearBlock(mBuf);
-        
-        if (recoveredMessage != null)
-        {
-            clearBlock(recoveredMessage);
-        }
-        
-        recoveredMessage = null;
-        fullMessage = false;
-    }
-
-    /**
-     * generate a signature for the loaded message using the key we were
-     * initialised with.
-     */
-    public byte[] generateSignature()
-        throws CryptoException
-    {
-        int     digSize = digest.getDigestSize();
-
-        int t = 0;
-        int delta = 0;
-
-        if (trailer == TRAILER_IMPLICIT)
-        {
-            t = 8;
-            delta = block.length - digSize - 1;
-            digest.doFinal(block, delta);
-            block[block.length - 1] = (byte)TRAILER_IMPLICIT;
-        }
-        else
-        {
-            t = 16;
-            delta = block.length - digSize - 2;
-            digest.doFinal(block, delta);
-            block[block.length - 2] = (byte)(trailer >>> 8);
-            block[block.length - 1] = (byte)trailer;
-        }
-
-        byte    header = 0;
-        int     x = (digSize + messageLength) * 8 + t + 4 - keyBits;
-
-        if (x > 0)
-        {
-            int mR = messageLength - ((x + 7) / 8);
-            header = 0x60;
-
-            delta -= mR;
-            
-            System.arraycopy(mBuf, 0, block, delta, mR);
-        }
-        else
-        {
-            header = 0x40;
-            delta -= messageLength;
-            
-            System.arraycopy(mBuf, 0, block, delta, messageLength);
-        }
-        
-        if ((delta - 1) > 0)
-        {
-            for (int i = delta - 1; i != 0; i--)
-            {
-                block[i] = (byte)0xbb;
-            }
-            block[delta - 1] ^= (byte)0x01;
-            block[0] = (byte)0x0b;
-            block[0] |= header;
-        }
-        else
-        {
-            block[0] = (byte)0x0a;
-            block[0] |= header;
-        }
-
-        byte[]  b = cipher.processBlock(block, 0, block.length);
-
-        clearBlock(mBuf);
-        clearBlock(block);
-
-        return b;
-    }
-
-    /**
-     * return true if the signature represents a ISO9796-2 signature
-     * for the passed in message.
-     */
-    public boolean verifySignature(
-        byte[]      signature)
-    {
-        byte[]      block = null;
-
-        try
-        {
-            block = cipher.processBlock(signature, 0, signature.length);
-        }
-        catch (Exception e)
-        {
-            return false;
-        }
-
-        if (((block[0] & 0xC0) ^ 0x40) != 0)
-        {
-            clearBlock(mBuf);
-            clearBlock(block);
-
-            return false;
-        }
-
-        if (((block[block.length - 1] & 0xF) ^ 0xC) != 0)
-        {
-            clearBlock(mBuf);
-            clearBlock(block);
-
-            return false;
-        }
-
-        int     delta = 0;
-
-        if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
-        {
-            delta = 1;
-        }
-        else
-        {
-            int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
-
-            switch (sigTrail)
-            {
-            // BEGIN android-removed
-            // case TRAILER_RIPEMD160:
-            //         if (!(digest instanceof RIPEMD160Digest))
-            //         {
-            //             throw new IllegalStateException("signer should be initialised with RIPEMD160");
-            //         }
-            //         break;
-            // END android-removed
-            case TRAILER_SHA1:
-                    if (!(digest instanceof SHA1Digest))
-                    {
-                        throw new IllegalStateException("signer should be initialised with SHA1");
-                    }
-                    break;
-            // BEGIN android-removed
-            // case TRAILER_RIPEMD128:
-            //         if (!(digest instanceof RIPEMD128Digest))
-            //         {
-            //             throw new IllegalStateException("signer should be initialised with RIPEMD128");
-            //         }
-            //         break;
-            // END android-removed
-            default:
-                throw new IllegalArgumentException("unrecognised hash in signature");
-            }
-
-            delta = 2;
-        }
-
-        //
-        // find out how much padding we've got
-        //
-        int mStart = 0;
-
-        for (mStart = 0; mStart != block.length; mStart++)
-        {
-            if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
-            {
-                break;
-            }
-        }
-
-        mStart++;
-
-        //
-        // check the hashes
-        //
-        byte[]  hash = new byte[digest.getDigestSize()];
-
-        int off = block.length - delta - hash.length;
-
-        //
-        // there must be at least one byte of message string
-        //
-        if ((off - mStart) <= 0)
-        {
-            clearBlock(mBuf);
-            clearBlock(block);
-
-            return false;
-        }
-
-        //
-        // if we contain the whole message as well, check the hash of that.
-        //
-        if ((block[0] & 0x20) == 0)
-        {
-            fullMessage = true;
-            
-            digest.reset();
-            digest.update(block, mStart, off - mStart);
-            digest.doFinal(hash, 0);
-            
-            for (int i = 0; i != hash.length; i++)
-            {
-                block[off + i] ^= hash[i];
-                if (block[off + i] != 0)
-                {
-                    clearBlock(mBuf);
-                    clearBlock(block);
-
-                    return false;
-                }
-            }
-            
-            recoveredMessage = new byte[off - mStart];
-            System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
-        }
-        else
-        {
-            fullMessage = false;
-            
-            digest.doFinal(hash, 0);
-            
-            for (int i = 0; i != hash.length; i++)
-            {
-                block[off + i] ^= hash[i];
-                if (block[off + i] != 0)
-                {
-                    clearBlock(mBuf);
-                    clearBlock(block);
-
-                    return false;
-                }
-            }
-            
-            recoveredMessage = new byte[off - mStart];
-            System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
-        }
-
-        //
-        // if they've input a message check what we've recovered against
-        // what was input.
-        //
-        if (messageLength != 0)
-        {
-            if (!isSameAs(mBuf, recoveredMessage))
-            {
-                clearBlock(mBuf);
-                clearBlock(block);
-
-                return false;
-            }
-        }
-        
-        clearBlock(mBuf);
-        clearBlock(block);
-
-        return true;
-    }
-
-    /**
-     * Return true if the full message was recoveredMessage.
-     * 
-     * @return true on full message recovery, false otherwise.
-     * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
-     */
-    public boolean hasFullMessage()
-    {
-        return fullMessage;
-    }
-
-    /**
-     * Return a reference to the recoveredMessage message.
-     * 
-     * @return the full/partial recoveredMessage message.
-     * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
-     */
-    public byte[] getRecoveredMessage()
-    {
-        return recoveredMessage;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java b/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
deleted file mode 100644
index ad4f53e..0000000
--- a/src/main/java/org/bouncycastle/crypto/signers/PSSSigner.java
+++ /dev/null
@@ -1,318 +0,0 @@
-package org.bouncycastle.crypto.signers;
-
-import java.security.SecureRandom;
-
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.CryptoException;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.Signer;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-
-/**
- * RSA-PSS as described in PKCS# 1 v 2.1.
- * <p>
- * Note: the usual value for the salt length is the number of
- * bytes in the hash function.
- */
-public class PSSSigner
-    implements Signer
-{
-    static final public byte   TRAILER_IMPLICIT    = (byte)0xBC;
-
-    private Digest                      digest;
-    private AsymmetricBlockCipher       cipher;
-    private SecureRandom                random;
-
-    private int                         hLen;
-    private int                         sLen;
-    private int                         emBits;
-    private byte[]                      salt;
-    private byte[]                      mDash;
-    private byte[]                      block;
-    private byte                        trailer;
-
-    /**
-     * basic constructor
-     *
-     * @param cipher the assymetric cipher to use.
-     * @param digest the digest to use.
-     * @param sLen the length of the salt to use (in bytes).
-     */
-    public PSSSigner(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest,
-        int                     sLen)
-    {
-        this(cipher, digest, sLen, TRAILER_IMPLICIT);
-    }
-    
-    public PSSSigner(
-        AsymmetricBlockCipher   cipher,
-        Digest                  digest,
-        int                     sLen,
-        byte                    trailer)
-    {
-        this.cipher = cipher;
-        this.digest = digest;
-        this.hLen = digest.getDigestSize();
-        this.sLen = sLen;
-        this.salt = new byte[sLen];
-        this.mDash = new byte[8 + sLen + hLen];
-        this.trailer = trailer;
-    }
-
-    public void init(
-        boolean                 forSigning,
-        CipherParameters        param)
-    {
-        RSAKeyParameters  kParam = null;
-
-        if (param instanceof ParametersWithRandom)
-        {
-            ParametersWithRandom    p = (ParametersWithRandom)param;
-
-            kParam = (RSAKeyParameters)p.getParameters();
-            random = p.getRandom();
-        }
-        else
-        {
-            kParam = (RSAKeyParameters)param;
-            if (forSigning)
-            {
-                random = new SecureRandom();
-            }
-        }
-
-        cipher.init(forSigning, kParam);
-
-        emBits = kParam.getModulus().bitLength() - 1;
-
-        block = new byte[(emBits + 7) / 8];
-
-        reset();
-    }
-
-    /**
-     * clear possible sensitive data
-     */
-    private void clearBlock(
-        byte[]  block)
-    {
-        for (int i = 0; i != block.length; i++)
-        {
-            block[i] = 0;
-        }
-    }
-
-    /**
-     * update the internal digest with the byte b
-     */
-    public void update(
-        byte    b)
-    {
-        digest.update(b);
-    }
-
-    /**
-     * update the internal digest with the byte array in
-     */
-    public void update(
-        byte[]  in,
-        int     off,
-        int     len)
-    {
-        digest.update(in, off, len);
-    }
-
-    /**
-     * reset the internal state
-     */
-    public void reset()
-    {
-        digest.reset();
-    }
-
-    /**
-     * generate a signature for the message we've been loaded with using
-     * the key we were initialised with.
-     */
-    public byte[] generateSignature()
-        throws CryptoException, DataLengthException
-    {
-        if (emBits < (8 * hLen + 8 * sLen + 9))
-        {
-            throw new DataLengthException("encoding error");
-        }
-
-        digest.doFinal(mDash, mDash.length - hLen - sLen);
-
-        if (sLen != 0)
-        {
-            random.nextBytes(salt);
-
-            System.arraycopy(salt, 0, mDash, mDash.length - sLen, sLen);
-        }
-
-        byte[]  h = new byte[hLen];
-
-        digest.update(mDash, 0, mDash.length);
-
-        digest.doFinal(h, 0);
-
-        block[block.length - sLen - 1 - hLen - 1] = 0x01;
-        System.arraycopy(salt, 0, block, block.length - sLen - hLen - 1, sLen);
-
-        byte[] dbMask = maskGeneratorFunction1(h, 0, h.length, block.length - hLen - 1);
-        for (int i = 0; i != dbMask.length; i++)
-        {
-            block[i] ^= dbMask[i];
-        }
-
-        block[0] &= (0xff >> ((block.length * 8) - emBits));
-
-        System.arraycopy(h, 0, block, block.length - hLen - 1, hLen);
-
-        block[block.length - 1] = trailer;
-
-        byte[]  b = cipher.processBlock(block, 0, block.length);
-
-        clearBlock(block);
-
-        return b;
-    }
-
-    /**
-     * return true if the internal state represents the signature described
-     * in the passed in array.
-     */
-    public boolean verifySignature(
-        byte[]      signature)
-    {
-        if (emBits < (8 * hLen + 8 * sLen + 9))
-        {
-            return false;
-        }
-
-        digest.doFinal(mDash, mDash.length - hLen - sLen);
-
-        try
-        {
-            byte[] b = cipher.processBlock(signature, 0, signature.length);
-            System.arraycopy(b, 0, block, block.length - b.length, b.length);
-        }
-        catch (Exception e)
-        {
-            return false;
-        }
-
-        if (block[block.length - 1] != trailer)
-        {
-            clearBlock(block);
-            return false;
-        }
-
-        byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - 1, hLen, block.length - hLen - 1);
-
-        for (int i = 0; i != dbMask.length; i++)
-        {
-            block[i] ^= dbMask[i];
-        }
-
-        block[0] &= (0xff >> ((block.length * 8) - emBits));
-
-        for (int i = 0; i != block.length - hLen - sLen - 2; i++)
-        {
-            if (block[i] != 0)
-            {
-                clearBlock(block);
-                return false;
-            }
-        }
-
-        if (block[block.length - hLen - sLen - 2] != 0x01)
-        {
-            clearBlock(block);
-            return false;
-        }
-
-        System.arraycopy(block, block.length - sLen - hLen - 1, mDash, mDash.length - sLen, sLen);
-
-        digest.update(mDash, 0, mDash.length);
-        digest.doFinal(mDash, mDash.length - hLen);
-
-        for (int i = block.length - hLen - 1, j = mDash.length - hLen;
-                                                 j != mDash.length; i++, j++)
-        {
-            if ((block[i] ^ mDash[j]) != 0)
-            {
-                clearBlock(mDash);
-                clearBlock(block);
-                return false;
-            }
-        }
-
-        clearBlock(mDash);
-        clearBlock(block);
-
-        return true;
-    }
-
-    /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
-     * mask generator function, as described in PKCS1v2.
-     */
-    private byte[] maskGeneratorFunction1(
-        byte[]  Z,
-        int     zOff,
-        int     zLen,
-        int     length)
-    {
-        byte[]  mask = new byte[length];
-        byte[]  hashBuf = new byte[hLen];
-        byte[]  C = new byte[4];
-        int     counter = 0;
-
-        digest.reset();
-
-        while (counter < (length / hLen))
-        {
-            ItoOSP(counter, C);
-
-            digest.update(Z, zOff, zLen);
-            digest.update(C, 0, C.length);
-            digest.doFinal(hashBuf, 0);
-
-            System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
-            
-            counter++;
-        }
-
-        if ((counter * hLen) < length)
-        {
-            ItoOSP(counter, C);
-
-            digest.update(Z, zOff, zLen);
-            digest.update(C, 0, C.length);
-            digest.doFinal(hashBuf, 0);
-
-            System.arraycopy(hashBuf, 0, mask, counter * hLen, mask.length - (counter * hLen));
-        }
-
-        return mask;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
new file mode 100644
index 0000000..58d1b87
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
@@ -0,0 +1,232 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+import java.util.Hashtable;
+
+public class RSADigestSigner
+    implements Signer
+{
+    private final AsymmetricBlockCipher rsaEngine = new PKCS1Encoding(new RSABlindedEngine());
+    private final AlgorithmIdentifier algId;
+    private final Digest digest;
+    private boolean forSigning;
+
+    private static final Hashtable oidMap = new Hashtable();
+
+    /*
+     * Load OID table.
+     */
+    static
+    {
+        oidMap.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
+        oidMap.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
+        oidMap.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
+
+        oidMap.put("SHA-1", X509ObjectIdentifiers.id_SHA1);
+        oidMap.put("SHA-224", NISTObjectIdentifiers.id_sha224);
+        oidMap.put("SHA-256", NISTObjectIdentifiers.id_sha256);
+        oidMap.put("SHA-384", NISTObjectIdentifiers.id_sha384);
+        oidMap.put("SHA-512", NISTObjectIdentifiers.id_sha512);
+
+        // BEGIN android-removed
+        // oidMap.put("MD2", PKCSObjectIdentifiers.md2);
+        // oidMap.put("MD4", PKCSObjectIdentifiers.md4);
+        // END android-removed
+        oidMap.put("MD5", PKCSObjectIdentifiers.md5);
+    }
+
+    public RSADigestSigner(
+        Digest digest)
+    {
+        this.digest = digest;
+
+        algId = new AlgorithmIdentifier((DERObjectIdentifier)oidMap.get(digest.getAlgorithmName()), DERNull.INSTANCE);
+    }
+
+    /**
+     * @deprecated
+     */
+    public String getAlgorithmName()
+    {
+        return digest.getAlgorithmName() + "withRSA";
+    }
+
+    /**
+     * initialise the signer for signing or verification.
+     *
+     * @param forSigning
+     *            true if for signing, false otherwise
+     * @param parameters
+     *            necessary parameters.
+     */
+    public void init(
+        boolean          forSigning,
+        CipherParameters parameters)
+    {
+        this.forSigning = forSigning;
+        AsymmetricKeyParameter k;
+
+        if (parameters instanceof ParametersWithRandom)
+        {
+            k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).getParameters();
+        }
+        else
+        {
+            k = (AsymmetricKeyParameter)parameters;
+        }
+
+        if (forSigning && !k.isPrivate())
+        {
+            throw new IllegalArgumentException("signing requires private key");
+        }
+
+        if (!forSigning && k.isPrivate())
+        {
+            throw new IllegalArgumentException("verification requires public key");
+        }
+
+        reset();
+
+        rsaEngine.init(forSigning, parameters);
+    }
+
+    /**
+     * update the internal digest with the byte b
+     */
+    public void update(
+        byte input)
+    {
+        digest.update(input);
+    }
+
+    /**
+     * update the internal digest with the byte array in
+     */
+    public void update(
+        byte[]  input,
+        int     inOff,
+        int     length)
+    {
+        digest.update(input, inOff, length);
+    }
+
+    /**
+     * Generate a signature for the message we've been loaded with using the key
+     * we were initialised with.
+     */
+    public byte[] generateSignature()
+        throws CryptoException, DataLengthException
+    {
+        if (!forSigning)
+        {
+            throw new IllegalStateException("RSADigestSigner not initialised for signature generation.");
+        }
+
+        byte[] hash = new byte[digest.getDigestSize()];
+        digest.doFinal(hash, 0);
+
+        byte[] data = derEncode(hash);
+        return rsaEngine.processBlock(data, 0, data.length);
+    }
+
+    /**
+     * return true if the internal state represents the signature described in
+     * the passed in array.
+     */
+    public boolean verifySignature(
+        byte[] signature)
+    {
+        if (forSigning)
+        {
+            throw new IllegalStateException("RSADigestSigner not initialised for verification");
+        }
+
+        byte[] hash = new byte[digest.getDigestSize()];
+        digest.doFinal(hash, 0);
+
+        byte[] sig;
+        byte[] expected;
+
+        try
+        {
+            sig = rsaEngine.processBlock(signature, 0, signature.length);
+            expected = derEncode(hash);
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+
+        if (sig.length == expected.length)
+        {
+            for (int i = 0; i < sig.length; i++)
+            {
+                if (sig[i] != expected[i])
+                {
+                    return false;
+                }
+            }
+        }
+        else if (sig.length == expected.length - 2)  // NULL left out
+        {
+            int sigOffset = sig.length - hash.length - 2;
+            int expectedOffset = expected.length - hash.length - 2;
+
+            expected[1] -= 2;      // adjust lengths
+            expected[3] -= 2;
+
+            for (int i = 0; i < hash.length; i++)
+            {
+                if (sig[sigOffset + i] != expected[expectedOffset + i])  // check hash
+                {
+                    return false;
+                }
+            }
+
+            for (int i = 0; i < sigOffset; i++)
+            {
+                if (sig[i] != expected[i])  // check header less NULL
+                {
+                    return false;
+                }
+            }
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    public void reset()
+    {
+        digest.reset();
+    }
+
+    private byte[] derEncode(
+        byte[] hash)
+    {
+        DigestInfo dInfo = new DigestInfo(algId, hash);
+
+        return dInfo.getDEREncoded();
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/util/Pack.java b/src/main/java/org/bouncycastle/crypto/util/Pack.java
new file mode 100644
index 0000000..12b5999
--- /dev/null
+++ b/src/main/java/org/bouncycastle/crypto/util/Pack.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.crypto.util;
+
+public abstract class Pack
+{
+    public static int bigEndianToInt(byte[] bs, int off)
+    {
+        int n = bs[  off] << 24;
+        n |= (bs[++off] & 0xff) << 16;
+        n |= (bs[++off] & 0xff) << 8;
+        n |= (bs[++off] & 0xff);
+        return n;
+    }
+
+    public static void intToBigEndian(int n, byte[] bs, int off)
+    {
+        bs[  off] = (byte)(n >>> 24);
+        bs[++off] = (byte)(n >>> 16);
+        bs[++off] = (byte)(n >>>  8);
+        bs[++off] = (byte)(n       );
+    }
+
+    public static long bigEndianToLong(byte[] bs, int off)
+    {
+        int hi = bigEndianToInt(bs, off);
+        int lo = bigEndianToInt(bs, off + 4);
+        return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL);
+    }
+
+    public static void longToBigEndian(long n, byte[] bs, int off)
+    {
+        intToBigEndian((int)(n >>> 32), bs, off);
+        intToBigEndian((int)(n & 0xffffffffL), bs, off + 4);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index bb14375..8f78e08 100644
--- a/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -1,12 +1,14 @@
 package org.bouncycastle.crypto.util;
 
-import java.io.IOException;
-
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 // BEGIN android-removed
+// import org.bouncycastle.asn1.nist.NISTNamedCurves;
 // import org.bouncycastle.asn1.oiw.ElGamalParameter;
 // END android-removed
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -16,6 +18,8 @@
 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
 // BEGIN android-removed
 // import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
+// import org.bouncycastle.asn1.sec.SECNamedCurves;
+// import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
 // END android-removed
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
@@ -23,8 +27,8 @@
 // import org.bouncycastle.asn1.x9.X962NamedCurves;
 // import org.bouncycastle.asn1.x9.X962Parameters;
 // import org.bouncycastle.asn1.x9.X9ECParameters;
+// import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 // END android-removed
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -35,15 +39,51 @@
 // import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 // import org.bouncycastle.crypto.params.ElGamalParameters;
 // import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
-//END android-removed
+// END android-removed
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
 /**
  * Factory for creating private key objects from PKCS8 PrivateKeyInfo objects.
  */
 public class PrivateKeyFactory
 {
     /**
+     * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding.
+     * 
+     * @param privateKeyInfoData the PrivateKeyInfo encoding
+     * @return a suitable private key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(
+        byte[] privateKeyInfoData)
+        throws IOException
+    {
+        return createKey(
+            PrivateKeyInfo.getInstance(
+                ASN1Object.fromByteArray(privateKeyInfoData)));
+    }
+
+    /**
+     * Create a private key parameter from a PKCS8 PrivateKeyInfo encoding read from a stream.
+     * 
+     * @param inStr the stream to read the PrivateKeyInfo encoding from
+     * @return a suitable private key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(
+        InputStream inStr)
+        throws IOException
+    {
+        return createKey(
+            PrivateKeyInfo.getInstance(
+                new ASN1InputStream(inStr).readObject()));
+    }
+
+    /**
      * Create a private key parameter from the passed in PKCS8 PrivateKeyInfo object.
      * 
      * @param keyInfo the PrivateKeyInfo object containing the key material
@@ -75,7 +115,11 @@
             DHParameter     params = new DHParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
             DERInteger      derX = (DERInteger)keyInfo.getPrivateKey();
 
-            return new DHPrivateKeyParameters(derX.getValue(), new DHParameters(params.getP(), params.getG()));
+            BigInteger lVal = params.getL();
+            int l = lVal == null ? 0 : lVal.intValue();
+            DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l);
+
+            return new DHPrivateKeyParameters(derX.getValue(), dhParams);
         }
         // BEGIN android-removed
         // else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
@@ -85,25 +129,45 @@
         //
         //     return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(params.getP(), params.getG()));
         // }
-        // END android-removed
-        else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa))
-        {
-            DSAParameter    params = new DSAParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
-            DERInteger      derX = (DERInteger)keyInfo.getPrivateKey();
-
-            return new DSAPrivateKeyParameters(derX.getValue(), new DSAParameters(params.getP(), params.getQ(), params.getG()));
-        }
-        // BEGIN android-removed
+        // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa))
+        // {
+        //     DERInteger derX = (DERInteger)keyInfo.getPrivateKey();
+        //     DEREncodable de = keyInfo.getAlgorithmId().getParameters();
+        //
+        //     DSAParameters parameters = null;
+        //     if (de != null)
+        //     {
+        //         DSAParameter params = DSAParameter.getInstance(de.getDERObject());
+        //         parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
+        //     }
+        //
+        //     return new DSAPrivateKeyParameters(derX.getValue(), parameters);
+        // }
         // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
         // {
         //     X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
         //     ECDomainParameters  dParams = null;
-        //    
+        //
         //     if (params.isNamedCurve())
         //     {
         //         DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
         //         X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
         //
+        //         if (ecP == null)
+        //         {
+        //             ecP = SECNamedCurves.getByOID(oid);
+        //
+        //             if (ecP == null)
+        //             {
+        //                 ecP = NISTNamedCurves.getByOID(oid);
+        //
+        //                 if (ecP == null)
+        //                 {
+        //                     ecP = TeleTrusTNamedCurves.getByOID(oid);
+        //                 }
+        //             }
+        //         }
+        //
         //         dParams = new ECDomainParameters(
         //                                     ecP.getCurve(),
         //                                     ecP.getG(),
diff --git a/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
index c666800..c0672b5 100644
--- a/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -1,20 +1,26 @@
 package org.bouncycastle.crypto.util;
 
-import java.io.IOException;
-
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DEROctetString;
 // BEGIN android-removed
+// import org.bouncycastle.asn1.nist.NISTNamedCurves;
 // import org.bouncycastle.asn1.oiw.ElGamalParameter;
 // END android-removed
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.sec.SECNamedCurves;
+// import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
+// END android-removed
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
 import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
@@ -40,6 +46,10 @@
 // END android-removed
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
 /**
  * Factory to create asymmetric public key parameters for asymmetric ciphers
  * from range of ASN.1 encoded SubjectPublicKeyInfo objects.
@@ -47,6 +57,38 @@
 public class PublicKeyFactory
 {
     /**
+     * Create a public key from a SubjectPublicKeyInfo encoding
+     * 
+     * @param keyInfoData the SubjectPublicKeyInfo encoding
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(
+        byte[] keyInfoData)
+        throws IOException
+    {
+        return createKey(
+            SubjectPublicKeyInfo.getInstance(
+                ASN1Object.fromByteArray(keyInfoData)));
+    }
+
+    /**
+     * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
+     * 
+     * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
+     * @return the appropriate key parameter
+     * @throws IOException on an error decoding the key
+     */
+    public static AsymmetricKeyParameter createKey(
+        InputStream inStr)
+        throws IOException
+    {
+        return createKey(
+            SubjectPublicKeyInfo.getInstance(
+                new ASN1InputStream(inStr).readObject()));
+    }
+
+    /**
      * Create a public key from the passed in SubjectPublicKeyInfo
      * 
      * @param keyInfo the SubjectPublicKeyInfo containing the key data
@@ -72,7 +114,11 @@
             DHParameter params = new DHParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
             DERInteger  derY = (DERInteger)keyInfo.getPublicKey();
             
-            return new DHPublicKeyParameters(derY.getValue(), new DHParameters(params.getP(), params.getG()));
+            BigInteger lVal = params.getL();
+            int l = lVal == null ? 0 : lVal.intValue();
+            DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l);
+
+            return new DHPublicKeyParameters(derY.getValue(), dhParams);
         }
         // BEGIN android-removed
         // else if (algId.getObjectId().equals(OIWObjectIdentifiers.elGamalAlgorithm))
@@ -86,22 +132,44 @@
         else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_dsa)
                  || algId.getObjectId().equals(OIWObjectIdentifiers.dsaWithSHA1))
         {
-            DSAParameter    params = new DSAParameter((ASN1Sequence)keyInfo.getAlgorithmId().getParameters());
-            DERInteger      derY = (DERInteger)keyInfo.getPublicKey();
+            DERInteger derY = (DERInteger)keyInfo.getPublicKey();
+            DEREncodable de = keyInfo.getAlgorithmId().getParameters();
 
-            return new DSAPublicKeyParameters(derY.getValue(), new DSAParameters(params.getP(), params.getQ(), params.getG()));
+            DSAParameters parameters = null;
+            if (de != null)
+            {
+                DSAParameter params = DSAParameter.getInstance(de.getDERObject());
+                parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
+            }
+
+            return new DSAPublicKeyParameters(derY.getValue(), parameters);
         }
         // BEGIN android-removed
         // else if (algId.getObjectId().equals(X9ObjectIdentifiers.id_ecPublicKey))
         // {
         //     X962Parameters      params = new X962Parameters((DERObject)keyInfo.getAlgorithmId().getParameters());
         //     ECDomainParameters  dParams = null;
-        //     
+        //
         //     if (params.isNamedCurve())
         //     {
         //         DERObjectIdentifier oid = (DERObjectIdentifier)params.getParameters();
         //         X9ECParameters      ecP = X962NamedCurves.getByOID(oid);
         //
+        //         if (ecP == null)
+        //         {
+        //             ecP = SECNamedCurves.getByOID(oid);
+        //
+        //             if (ecP == null)
+        //             {
+        //                 ecP = NISTNamedCurves.getByOID(oid);
+        //
+        //                 if (ecP == null)
+        //                 {
+        //                     ecP = TeleTrusTNamedCurves.getByOID(oid);
+        //                 }
+        //             }
+        //         }
+        //
         //         dParams = new ECDomainParameters(
         //                                     ecP.getCurve(),
         //                                     ecP.getG(),
@@ -126,10 +194,10 @@
         //     ASN1OctetString key = new DEROctetString(data);
         //
         //     X9ECPoint       derQ = new X9ECPoint(dParams.getCurve(), key);
-        //    
+        //
         //     return new ECPublicKeyParameters(derQ.getPoint(), dParams);
         // }
-        // BEGIN android-removed
+        // END android-removed
         else
         {
             throw new RuntimeException("algorithm identifier in key not recognised");
diff --git a/src/main/java/org/bouncycastle/i18n/ErrorBundle.java b/src/main/java/org/bouncycastle/i18n/ErrorBundle.java
deleted file mode 100644
index 803abed..0000000
--- a/src/main/java/org/bouncycastle/i18n/ErrorBundle.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.bouncycastle.i18n;
-
-import java.util.Locale;
-import java.util.TimeZone;
-
-public class ErrorBundle extends MessageBundle 
-{
-
-    /**
-     * summary entry key
-     */
-    public static final String SUMMARY_ENTRY = "summary";
-    
-    /**
-     * detail entry key
-     */
-    public static final String DETAIL_ENTRY = "details";
-    
-    /**
-     * Constructs a new ErrorBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public ErrorBundle(String resource, String id) throws NullPointerException
-    {
-        super(resource, id);
-    }
-
-    /**
-     * Constructs a new ErrorBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @param arguments an array containing the arguments for the message
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public ErrorBundle(String resource, String id, Object[] arguments) throws NullPointerException
-    {
-        super(resource, id, arguments);
-    }
-    
-    /**
-     * Returns the summary message in the given locale and timezone.
-     * @param loc the {@link Locale}
-     * @param timezone the {@link TimeZone}
-     * @return the summary message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getSummary(Locale loc, TimeZone timezone) throws MissingEntryException
-    {
-        return getEntry(SUMMARY_ENTRY,loc,timezone);
-    }
-    
-    /**
-     * Returns the summary message in the given locale and the default timezone.
-     * @param loc the {@link Locale}
-     * @return the summary message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getSummary(Locale loc) throws MissingEntryException
-    {
-        return getEntry(SUMMARY_ENTRY,loc,TimeZone.getDefault());
-    }
-    
-    /**
-     * Returns the detail message in the given locale and timezone.
-     * @param loc the {@link Locale}
-     * @param timezone the {@link TimeZone}
-     * @return the detail message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getDetail(Locale loc, TimeZone timezone) throws MissingEntryException
-    {
-        return getEntry(DETAIL_ENTRY,loc,timezone);
-    }
-    
-    /**
-     * Returns the detail message in the given locale and the default timezone.
-     * @param loc the {@link Locale}
-     * @return the detail message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getDetail(Locale loc) throws MissingEntryException
-    {
-        return getEntry(DETAIL_ENTRY,loc,TimeZone.getDefault());
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/LocalizedException.java b/src/main/java/org/bouncycastle/i18n/LocalizedException.java
deleted file mode 100644
index 373fd6c..0000000
--- a/src/main/java/org/bouncycastle/i18n/LocalizedException.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.bouncycastle.i18n;
-
-import java.util.Locale;
-
-/**
- * Base class for all Exceptions with localized messages.
- */
-public class LocalizedException extends Exception 
-{
-
-    protected ErrorBundle message;
-    private Throwable cause;
-    
-    /**
-     * Constructs a new LocalizedException with the specified localized message.
-     * @param message the {@link ErrorBundle} that contains the message for the exception
-     */
-    public LocalizedException(ErrorBundle message) 
-    {
-        super(message.getText(Locale.getDefault()));
-        this.message = message;
-    }
-    
-    /**
-     * Constructs a new LocalizedException with the specified localized message and cause.
-     * @param message the {@link ErrorBundle} that contains the message for the exception
-     * @param throwable the cause
-     */
-    public LocalizedException(ErrorBundle message, Throwable throwable) 
-    {
-        super(message.getText(Locale.getDefault()));
-        this.message = message;
-        this.cause = throwable;
-    }
-    
-    /**
-     * Returns the localized error message of the exception.
-     * @return the localized error message as {@link ErrorBundle}
-     */
-    public ErrorBundle getErrorMessage() 
-    {
-        return message;
-    }
-
-    public Throwable getCause()
-    {
-        return cause;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java b/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java
deleted file mode 100644
index 63825dd..0000000
--- a/src/main/java/org/bouncycastle/i18n/LocalizedMessage.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package org.bouncycastle.i18n;
-
-import java.text.DateFormat;
-import java.text.Format;
-import java.text.MessageFormat;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.TimeZone;
-
-import org.bouncycastle.i18n.filter.Filter;
-import org.bouncycastle.i18n.filter.UntrustedInput;
-
-public class LocalizedMessage 
-{
-
-    protected final String id;
-    protected final String resource;
-    
-    protected Object[] arguments;
-    protected Object[] filteredArguments;
-    
-    protected Filter filter = null;
-    
-    /**
-     * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public LocalizedMessage(String resource,String id) throws NullPointerException
-    {
-        if (resource == null || id == null)
-        {
-            throw new NullPointerException();
-        }
-        this.id = id;
-        this.resource = resource;
-        this.arguments = new Object[0];
-        this.filteredArguments = arguments;
-    }
-    
-    /**
-     * Constructs a new LocalizedMessage using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @param arguments an array containing the arguments for the message
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public LocalizedMessage(String resource, String id, Object[] arguments) throws NullPointerException
-    {
-        if (resource == null || id == null || arguments == null)
-        {
-            throw new NullPointerException();
-        }
-        this.id = id;
-        this.resource = resource;
-        this.arguments = arguments;
-        this.filteredArguments = arguments;
-    }
-    
-    /**
-     * Reads the entry <code>id + "." + key</code> from the resource file and returns a 
-     * formatted message for the given Locale and TimeZone.
-     * @param key second part of the entry id
-     * @param loc the used {@link Locale}
-     * @param timezone the used {@link TimeZone}
-     * @return a Strng containing the localized message
-     * @throws MissingEntryException if the resource file is not available or the entry does not exist.
-     */
-    public String getEntry(String key,Locale loc, TimeZone timezone) throws MissingEntryException
-    {
-        String entry = id + "." + key;
-        
-        try
-        {
-            ResourceBundle bundle = ResourceBundle.getBundle(resource,loc);
-            String template = bundle.getString(entry);
-            if (arguments == null || arguments.length == 0)
-            {
-                return template;
-            }
-            else
-            {
-                return formatWithTimeZone(template,filteredArguments,loc,timezone);
-            }
-        }
-        catch (MissingResourceException mre)
-        {
-            throw new MissingEntryException("Can't find entry " + entry + " in resource file " + resource + ".",
-                    resource,
-                    entry); 
-        }
-    }
-    
-    protected String formatWithTimeZone(
-            String template,
-            Object[] arguments, 
-            Locale locale,
-            TimeZone timezone) 
-    {
-        MessageFormat mf = new MessageFormat(" ");
-        mf.setLocale(locale);
-        mf.applyPattern(template);
-        if (!timezone.equals(TimeZone.getDefault())) 
-        {
-            Format[] formats = mf.getFormats();
-            for (int i = 0; i < formats.length; i++) 
-            {
-                if (formats[i] instanceof DateFormat) 
-                {
-                    DateFormat temp = (DateFormat) formats[i];
-                    temp.setTimeZone(timezone);
-                    mf.setFormat(i,temp);
-                }
-            }
-        }
-        return mf.format(arguments);
-    }
-    
-    /**
-     * Sets the {@link Filter} that is used to filter the arguments of this message
-     * @param filter the {@link Filter} to use. <code>null</code> to disable filtering.
-     */
-    public void setFilter(Filter filter)
-    {
-        if (filter == null)
-        {
-            filteredArguments = arguments;
-        }
-        else if (!filter.equals(this.filter))
-        {
-            filteredArguments = new Object[arguments.length];
-            for (int i = 0; i < arguments.length; i++)
-            {
-                if (arguments[i] instanceof UntrustedInput) 
-                {
-                    filteredArguments[i] = filter.doFilter(((UntrustedInput) arguments[i]).getString());
-                }
-                else
-                {
-                    filteredArguments[i] = arguments[i];
-                }
-            }
-        }
-        this.filter = filter;
-    }
-    
-    /**
-     * Returns the current filter.
-     * @return the current filter
-     */
-    public Filter getFilter()
-    {
-        return filter;
-    }
-    
-    /**
-     * Returns the id of the message in the resource bundle.
-     * @return the id of the message
-     */
-    public String getId()
-    {
-        return id;
-    }
-    
-    /**
-     * Returns the name of the resource bundle for this message
-     * @return name of the resource file
-     */
-    public String getResource()
-    {
-        return resource;
-    }
-    
-    /**
-     * Returns an <code>Object[]</code> containing the message arguments.
-     * @return the message arguments
-     */
-    public Object[] getArguments()
-    {
-        return arguments;
-    }
-    
-}
diff --git a/src/main/java/org/bouncycastle/i18n/MessageBundle.java b/src/main/java/org/bouncycastle/i18n/MessageBundle.java
deleted file mode 100644
index 8848f0f..0000000
--- a/src/main/java/org/bouncycastle/i18n/MessageBundle.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.bouncycastle.i18n;
-
-import java.util.Locale;
-import java.util.TimeZone;
-
-public class MessageBundle extends TextBundle
-{
-
-    /**
-     * title entry key
-     */
-    public static final String TITLE_ENTRY = "title";
-    
-    /**
-     * Constructs a new MessageBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public MessageBundle(String resource, String id) throws NullPointerException
-    {
-        super(resource, id);
-    }
-
-    /**
-     * Constructs a new MessageBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @param arguments an array containing the arguments for the message
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public MessageBundle(String resource, String id, Object[] arguments) throws NullPointerException
-    {
-        super(resource, id, arguments);
-    }
-    
-    /**
-     * Returns the title message in the given locale and timezone.
-     * @param loc the {@link Locale}
-     * @param timezone the {@link TimeZone}
-     * @return the title message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getTitle(Locale loc,TimeZone timezone) throws MissingEntryException
-    {
-        return getEntry(TITLE_ENTRY,loc,timezone);
-    }
-    
-    /**
-     * Returns the title message in the given locale and the default timezone.
-     * @param loc the {@link Locale}
-     * @return the title message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getTitle(Locale loc) throws MissingEntryException
-    {
-        return getEntry(TITLE_ENTRY,loc,TimeZone.getDefault());
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/MissingEntryException.java b/src/main/java/org/bouncycastle/i18n/MissingEntryException.java
deleted file mode 100644
index 11dfdc4..0000000
--- a/src/main/java/org/bouncycastle/i18n/MissingEntryException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.bouncycastle.i18n;
-
-public class MissingEntryException extends RuntimeException 
-{
-
-    protected final String resource;
-    protected final String key;
-
-    public MissingEntryException(String message, String resource, String key) 
-    {
-        super(message);
-        this.resource = resource;
-        this.key = key;
-    }
-
-    public String getKey()
-    {
-        return key;
-    }
-
-    public String getResource()
-    {
-        return resource;
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/TextBundle.java b/src/main/java/org/bouncycastle/i18n/TextBundle.java
deleted file mode 100644
index 9d981d9..0000000
--- a/src/main/java/org/bouncycastle/i18n/TextBundle.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.bouncycastle.i18n;
-
-import java.util.Locale;
-import java.util.TimeZone;
-
-public class TextBundle extends LocalizedMessage 
-{
-
-    /**
-     * text entry key
-     */
-    public static final String TEXT_ENTRY = "text";
-    
-    /**
-     * Constructs a new TextBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public TextBundle(String resource, String id) throws NullPointerException 
-    {
-        super(resource, id);
-    }
-
-    /**
-     * Constructs a new TextBundle using <code>resource</code> as the base name for the 
-     * RessourceBundle and <code>id</code> as the message bundle id the resource file. 
-     * @param resource base name of the resource file 
-     * @param id the id of the corresponding bundle in the resource file
-     * @param arguments an array containing the arguments for the message
-     * @throws NullPointerException if <code>resource</code> or <code>id</code> is <code>null</code>
-     */
-    public TextBundle(String resource, String id, Object[] arguments) throws NullPointerException 
-    {
-        super(resource, id, arguments);
-    }
-    
-    /**
-     * Returns the text message in the given locale and timezone.
-     * @param loc the {@link Locale}
-     * @param timezone the {@link TimeZone}
-     * @return the text message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getText(Locale loc, TimeZone timezone) throws MissingEntryException
-    {
-        return getEntry(TEXT_ENTRY,loc,timezone);
-    }
-    
-    /**
-     * Returns the text message in the given locale and the defaut timezone.
-     * @param loc the {@link Locale}
-     * @return the text message.
-     * @throws MissingEntryException if the message is not available
-     */
-    public String getText(Locale loc) throws MissingEntryException
-    {
-        return getEntry(TEXT_ENTRY,loc,TimeZone.getDefault());
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/filter/Filter.java b/src/main/java/org/bouncycastle/i18n/filter/Filter.java
deleted file mode 100644
index aee58cb..0000000
--- a/src/main/java/org/bouncycastle/i18n/filter/Filter.java
+++ /dev/null
@@ -1,14 +0,0 @@
-
-package org.bouncycastle.i18n.filter;
-
-public interface Filter
-{
-
-    /**
-     * Runs the filter on the input String and returns the filtered String
-     * @param input input String
-     * @return filtered String
-     */
-    public String doFilter(String input);
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java b/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java
deleted file mode 100644
index defc610..0000000
--- a/src/main/java/org/bouncycastle/i18n/filter/HTMLFilter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-
-package org.bouncycastle.i18n.filter;
-
-/**
- * HTML Filter
- */
-public class HTMLFilter implements Filter 
-{
-
-    public String doFilter(String input) 
-    {
-        StringBuffer buf = new StringBuffer(input);
-        int i = 0;
-        while (i < buf.length()) 
-        {
-            char ch = buf.charAt(i);
-            switch (ch)
-            {
-            case '<':
-                buf.replace(i,i+1,"&#60");
-                break;
-            case '>':
-                buf.replace(i,i+1,"&#62");
-                break;
-            case '(':
-                buf.replace(i,i+1,"&#40");
-                break;
-            case ')':
-                buf.replace(i,i+1,"&#41");
-                break;
-            case '#':
-                buf.replace(i,i+1,"&#35");
-                break;
-            case '&':
-                buf.replace(i,i+1,"&#38");
-                break;
-            case '\"':
-                buf.replace(i,i+1,"&#34");
-                break;
-            case '\'':
-                buf.replace(i,i+1,"&#39");
-                break;
-            case '%':
-                buf.replace(i,i+1,"&#37");
-                break;
-            case ';':
-                buf.replace(i,i+1,"&#59");
-                break;
-            case '+':
-                buf.replace(i,i+1,"&#43");
-                break;
-            case '-':
-                buf.replace(i,i+1,"&#45");
-                break;
-            default:
-                i -= 3;
-            }
-            i += 4;
-        }
-        return buf.toString();
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java b/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java
deleted file mode 100644
index ed3e488..0000000
--- a/src/main/java/org/bouncycastle/i18n/filter/SQLFilter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-
-package org.bouncycastle.i18n.filter;
-
-/**
- * Filter for strings to store in a SQL table.
- * 
- * escapes ' " = - / \ ; \r \n
- */
-public class SQLFilter implements Filter
-{
-
-    public String doFilter(String input) 
-    {
-        StringBuffer buf = new StringBuffer(input);
-        int i = 0;
-        while (i < buf.length()) 
-        {
-            char ch = buf.charAt(i);
-            switch (ch) 
-            {
-            case '\'':
-                buf.replace(i,i+1,"\\\'");
-                i += 1;
-                break;
-            case '\"':
-                buf.replace(i,i+1,"\\\"");
-                i += 1;
-                break;
-            case '=':
-                buf.replace(i,i+1,"\\=");
-                i += 1;
-                break;
-            case '-':
-                buf.replace(i,i+1,"\\-");
-                i += 1;
-                break;
-            case '/':
-                buf.replace(i,i+1,"\\/");
-                i += 1;
-                break;
-            case '\\':
-                buf.replace(i,i+1,"\\\\");
-                i += 1;
-                break;
-            case ';':
-                buf.replace(i,i+1,"\\;");
-                i += 1;
-                break;
-            case '\r':
-                buf.replace(i,i+1,"\\r");
-                i += 1;
-                break;
-            case '\n':
-                buf.replace(i,i+1,"\\n");
-                i += 1;
-                break;
-            default:
-            }
-            i++;
-        }
-        return buf.toString();
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java b/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java
deleted file mode 100644
index cc69ac4..0000000
--- a/src/main/java/org/bouncycastle/i18n/filter/UntrustedInput.java
+++ /dev/null
@@ -1,44 +0,0 @@
-
-package org.bouncycastle.i18n.filter;
-
-/**
- * Wrapper class to mark untrusted input.
- */
-public class UntrustedInput 
-{
-
-    protected Object input;
-
-    /**
-     * Construct a new UntrustedInput instance.
-     * @param input the untrusted input Object
-     */
-    public UntrustedInput(Object input) 
-    {
-        this.input = input;
-    }
-
-    /**
-     * Returns the untrusted input as Object.
-     * @return the <code>input</code> as Object
-     */
-    public Object getInput() 
-    {
-        return input;
-    }
-
-    /**
-     * Returns the untrusted input convertet to a String.
-     * @return the <code>input</code> as String
-     */
-    public String getString() 
-    {
-        return input.toString();
-    }
-    
-    public String toString()
-    {
-        return input.toString();
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java b/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
index f90ffa4..cdca7a9 100644
--- a/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
+++ b/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java
@@ -1,8 +1,33 @@
 package org.bouncycastle.jce;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.CertificationRequest;
+import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.util.Strings;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -12,30 +37,11 @@
 import java.security.Signature;
 import java.security.SignatureException;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PSSParameterSpec;
 import java.security.spec.X509EncodedKeySpec;
-import java.util.Set;
 import java.util.HashSet;
 import java.util.Hashtable;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.CertificationRequest;
-import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
-import org.bouncycastle.util.Strings;
+import java.util.Set;
 
 /**
  * A class for verifying and creating PKCS10 Certification requests. 
@@ -65,6 +71,7 @@
     extends CertificationRequest
 {
     private static Hashtable            algorithms = new Hashtable();
+    private static Hashtable            params = new Hashtable();
     private static Hashtable            keyAlgorithms = new Hashtable();
     private static Hashtable            oids = new Hashtable();
     private static Set                  noParams = new HashSet();
@@ -89,6 +96,11 @@
         algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
         algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
         algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("RSAWITHSHA1", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
         algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
         algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
@@ -105,7 +117,8 @@
         algorithms.put("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         algorithms.put("GOST3410WITHGOST3411", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
-        //algorithms.put("ECGOST3410WITHGOST3411", 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);
 
         //
         // reverse mappings
@@ -116,7 +129,7 @@
         oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
         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(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
         
         oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
         // BEGIN android-removed
@@ -152,6 +165,48 @@
         noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
+
+        //
+        // RFC 4491
+        //
+        noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
+        noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        //
+        // explicit params
+        //
+        // BEGIN android-changed
+        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA1WITHRSAANDMGF1", creatPSSParams(sha1AlgId, 20));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA224WITHRSAANDMGF1", creatPSSParams(sha224AlgId, 28));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA256WITHRSAANDMGF1", creatPSSParams(sha256AlgId, 32));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA384WITHRSAANDMGF1", creatPSSParams(sha384AlgId, 48));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA512WITHRSAANDMGF1", creatPSSParams(sha512AlgId, 64));
+    }
+
+    private static RSASSAPSSparams creatPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
+    {
+        return new RSASSAPSSparams(
+            hashAlgId,
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId),
+            new DERInteger(saltSize),
+            new DERInteger(1));
     }
 
     private static ASN1Sequence toDERSequence(
@@ -159,8 +214,7 @@
     {
         try
         {
-            ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
-            ASN1InputStream         dIn = new ASN1InputStream(bIn);
+            ASN1InputStream         dIn = new ASN1InputStream(bytes);
 
             return (ASN1Sequence)dIn.readObject();
         }
@@ -258,7 +312,8 @@
         throws NoSuchAlgorithmException, NoSuchProviderException,
                 InvalidKeyException, SignatureException
     {
-        DERObjectIdentifier sigOID = (DERObjectIdentifier)algorithms.get(Strings.toUpperCase(signatureAlgorithm));
+        String algorithmName = Strings.toUpperCase(signatureAlgorithm);
+        DERObjectIdentifier sigOID = (DERObjectIdentifier)algorithms.get(algorithmName);
 
         if (sigOID == null)
         {
@@ -279,31 +334,31 @@
         {
             this.sigAlgId = new AlgorithmIdentifier(sigOID);
         }
+        else if (params.containsKey(algorithmName))
+        {
+            this.sigAlgId = new AlgorithmIdentifier(sigOID, (DEREncodable)params.get(algorithmName));
+        }
         else
         {
             this.sigAlgId = new AlgorithmIdentifier(sigOID, null);
         }
 
-        byte[]                  bytes = key.getEncoded();
-        ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
-        ASN1InputStream         dIn = new ASN1InputStream(bIn);
-
         try
         {
-            this.reqInfo = new CertificationRequestInfo(subject, new SubjectPublicKeyInfo((ASN1Sequence)dIn.readObject()), attributes);
+            ASN1Sequence seq = (ASN1Sequence)ASN1Object.fromByteArray(key.getEncoded());
+            this.reqInfo = new CertificationRequestInfo(subject, new SubjectPublicKeyInfo(seq), attributes);
         }
         catch (IOException e)
         {
             throw new IllegalArgumentException("can't encode public key");
         }
 
-        Signature sig = null;
-        
-        try
+        Signature sig;
+        if (provider == null)
         {
-            sig = Signature.getInstance(sigAlgId.getObjectId().getId(), provider);
+            sig = Signature.getInstance(signatureAlgorithm);
         }
-        catch (NoSuchAlgorithmException e)
+        else
         {
             sig = Signature.getInstance(signatureAlgorithm, provider);
         }
@@ -312,16 +367,11 @@
 
         try
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(reqInfo);
-
-            sig.update(bOut.toByteArray());
+            sig.update(reqInfo.getEncoded(ASN1Encodable.DER));
         }
         catch (Exception e)
         {
-            throw new SecurityException("exception encoding TBS cert request - " + e);
+            throw new IllegalArgumentException("exception encoding TBS cert request - " + e);
         }
 
         this.sigBits = new DERBitString(sig.sign());
@@ -350,7 +400,14 @@
         {
             try
             {
-                return KeyFactory.getInstance(keyAlg.getObjectId().getId(), provider).generatePublic(xspec);
+                if (provider == null)
+                {
+                    return KeyFactory.getInstance(keyAlg.getObjectId().getId()).generatePublic(xspec);
+                }
+                else
+                {
+                    return KeyFactory.getInstance(keyAlg.getObjectId().getId(), provider).generatePublic(xspec);
+                }
             }
             catch (NoSuchAlgorithmException e)
             {
@@ -361,7 +418,14 @@
                 {
                     String  keyAlgorithm = (String)keyAlgorithms.get(keyAlg.getObjectId());
                     
-                    return KeyFactory.getInstance(keyAlgorithm, provider).generatePublic(xspec);
+                    if (provider == null)
+                    {
+                        return KeyFactory.getInstance(keyAlgorithm).generatePublic(xspec);
+                    }
+                    else
+                    {
+                        return KeyFactory.getInstance(keyAlgorithm, provider).generatePublic(xspec);
+                    }
                 }
                 
                 throw e;
@@ -383,16 +447,38 @@
         return verify("BC");
     }
 
+    /**
+     * verify the request using the passed in provider.
+     */
     public boolean verify(
         String provider)
         throws NoSuchAlgorithmException, NoSuchProviderException,
                 InvalidKeyException, SignatureException
     {
-        Signature   sig = null;
+        return verify(this.getPublicKey(provider), provider);
+    }
+
+    /**
+     * verify the request using the passed in public key and the provider..
+     */
+    public boolean verify(
+        PublicKey pubKey,
+        String provider)
+        throws NoSuchAlgorithmException, NoSuchProviderException,
+                InvalidKeyException, SignatureException
+    {
+        Signature   sig;
 
         try
         {
-            sig = Signature.getInstance(sigAlgId.getObjectId().getId(), provider);
+            if (provider == null)
+            {
+                sig = Signature.getInstance(getSignatureName(sigAlgId));
+            }
+            else
+            {
+                sig = Signature.getInstance(getSignatureName(sigAlgId), provider);
+            }
         }
         catch (NoSuchAlgorithmException e)
         {
@@ -403,7 +489,14 @@
             {
                 String  signatureAlgorithm = (String)oids.get(sigAlgId.getObjectId());
 
-                sig = Signature.getInstance(signatureAlgorithm, provider);
+                if (provider == null)
+                {
+                    sig = Signature.getInstance(signatureAlgorithm);
+                }
+                else
+                {
+                    sig = Signature.getInstance(signatureAlgorithm, provider);
+                }
             }
             else
             {
@@ -411,20 +504,17 @@
             }
         }
 
-        sig.initVerify(this.getPublicKey(provider));
+        setSignatureParameters(sig, sigAlgId.getParameters());
+        
+        sig.initVerify(pubKey);
 
         try
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(reqInfo);
-
-            sig.update(bOut.toByteArray());
+            sig.update(reqInfo.getEncoded(ASN1Encodable.DER));
         }
         catch (Exception e)
         {
-            throw new SecurityException("exception encoding TBS cert request - " + e);
+            throw new SignatureException("exception encoding TBS cert request - " + e);
         }
 
         return sig.verify(sigBits.getBytes());
@@ -435,18 +525,111 @@
      */
     public byte[] getEncoded()
     {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(this);
+            return this.getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
             throw new RuntimeException(e.toString());
         }
+    }
 
-        return bOut.toByteArray();
+    private void setSignatureParameters(
+        Signature signature,
+        DEREncodable params)
+        throws NoSuchAlgorithmException, SignatureException, InvalidKeyException
+    {
+        if (params != null && !DERNull.INSTANCE.equals(params))
+        {
+            AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider());
+
+            try
+            {
+                sigParams.init(params.getDERObject().getDEREncoded());
+            }
+            catch (IOException e)
+            {
+                throw new SignatureException("IOException decoding parameters: " + e.getMessage());
+            }
+
+            if (signature.getAlgorithm().endsWith("MGF1"))
+            {
+                try
+                {
+                    signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class));
+                }
+                catch (GeneralSecurityException e)
+                {
+                    throw new SignatureException("Exception extracting parameters: " + e.getMessage());
+                }
+            }
+        }
+    }
+
+    static String getSignatureName(
+        AlgorithmIdentifier sigAlgId)
+    {
+        DEREncodable params = sigAlgId.getParameters();
+
+        if (params != null && !DERNull.INSTANCE.equals(params))
+        {
+            if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            {
+                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+                return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
+            }
+        }
+
+        return sigAlgId.getObjectId().getId();
+    }
+
+    private static String getDigestAlgName(
+        DERObjectIdentifier digestAlgOID)
+    {
+        if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
+        {
+            return "MD5";
+        }
+        else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
+        {
+            return "SHA1";
+        }
+        else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
+        {
+            return "SHA224";
+        }
+        else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
+        {
+            return "SHA256";
+        }
+        else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID))
+        {
+            return "SHA384";
+        }
+        else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID))
+        {
+            return "SHA512";
+        }
+        else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
+        {
+            return "RIPEMD128";
+        }
+        else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
+        {
+            return "RIPEMD160";
+        }
+        else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
+        {
+            return "RIPEMD256";
+        }
+        else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
+        {
+            return "GOST3411";
+        }
+        else
+        {
+            return digestAlgOID.getId();            
+        }
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/PKCS7SignedData.java b/src/main/java/org/bouncycastle/jce/PKCS7SignedData.java
deleted file mode 100644
index cd05b9f..0000000
--- a/src/main/java/org/bouncycastle/jce/PKCS7SignedData.java
+++ /dev/null
@@ -1,600 +0,0 @@
-package org.bouncycastle.jce;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CRL;
-import java.security.cert.CRLException;
-import java.security.cert.Certificate;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.bouncycastle.asn1.*;
-import org.bouncycastle.asn1.pkcs.ContentInfo;
-import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.SignedData;
-import org.bouncycastle.asn1.pkcs.SignerInfo;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.CertificateList;
-import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.provider.X509CRLObject;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-
-/**
- * Represents a PKCS#7 object - specifically the "Signed Data"
- * type.
- * <p>
- * How to use it? To verify a signature, do:
- * <pre>
- * PKCS7SignedData pkcs7 = new PKCS7SignedData(der_bytes);        // Create it
- * pkcs7.update(bytes, 0, bytes.length);                          // Update checksum
- * boolean verified = pkcs7.verify();                             // Does it add up?
- *
- * To sign, do this:
- * PKCS7SignedData pkcs7 = new PKCS7SignedData(privKey, certChain, "MD5");
- * pkcs7.update(bytes, 0, bytes.length);                          // Update checksum
- * pkcs7.sign();                                                  // Create digest
- *
- * bytes = pkcs7.getEncoded();                                    // Write it somewhere
- * </pre>
- * <p>
- * This class is pretty close to obsolete, for a much better (and more complete)
- * implementation of PKCS7 have a look at the org.bouncycastle.cms package.
- * @deprecated this class really is obsolete - use the CMS package.
- */
-public class PKCS7SignedData
-    implements PKCSObjectIdentifiers
-{
-    private int version, signerversion;
-    private Set digestalgos;
-    private Collection certs, crls;
-    private X509Certificate signCert;
-    private byte[] digest;
-    private String digestAlgorithm, digestEncryptionAlgorithm;
-    private Signature sig;
-    private transient PrivateKey privKey;
-
-    private final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
-    private final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
-    private final String ID_MD5 = "1.2.840.113549.2.5";
-    private final String ID_MD2 = "1.2.840.113549.2.2";
-    private final String ID_SHA1 = "1.3.14.3.2.26";
-    private final String ID_RSA = "1.2.840.113549.1.1.1";
-    private final String ID_DSA = "1.2.840.10040.4.1";
-
-    /**
-     * Read an existing PKCS#7 object from a DER encoded byte array using
-     * the BC provider.
-     */
-    public PKCS7SignedData(
-        byte[]  in)
-        throws SecurityException, CRLException, InvalidKeyException,
-        NoSuchProviderException, NoSuchAlgorithmException
-    {
-        this(in, "BC");
-    }
-
-    /**
-     * Read an existing PKCS#7 object from a DER encoded byte array 
-     */
-    public PKCS7SignedData(
-        byte[]  in,
-        String  provider)
-        throws SecurityException, CRLException, InvalidKeyException,
-        NoSuchProviderException, NoSuchAlgorithmException
-    {
-        ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(in));
-
-        //
-        // Basic checks to make sure it's a PKCS#7 SignedData Object
-        //
-        DERObject pkcs;
-
-        try
-        {
-            pkcs = din.readObject();
-        }
-        catch (IOException e)
-        {
-            throw new SecurityException("can't decode PKCS7SignedData object");
-        }
-
-        if (!(pkcs instanceof ASN1Sequence))
-        {
-            throw new SecurityException("Not a valid PKCS#7 object - not a sequence");
-        }
-
-        ContentInfo content = ContentInfo.getInstance(pkcs);
-
-        if (!content.getContentType().equals(signedData))
-        {
-            throw new SecurityException("Not a valid PKCS#7 signed-data object - wrong header " + content.getContentType().getId());
-        }
-
-
-        SignedData  data = SignedData.getInstance(content.getContent());
-
-        certs = new ArrayList();
-
-        if (data.getCertificates() != null)
-        {
-            Enumeration ec = ASN1Set.getInstance(data.getCertificates()).getObjects();
-
-            while (ec.hasMoreElements())
-            {
-                certs.add(new X509CertificateObject(X509CertificateStructure.getInstance(ec.nextElement())));
-            }
-        }
-
-        crls = new ArrayList();
-
-        if (data.getCRLs() != null)
-        {
-            Enumeration ec = ASN1Set.getInstance(data.getCRLs()).getObjects();
-            while (ec.hasMoreElements())
-            {
-                crls.add(new X509CRLObject(CertificateList.getInstance(ec.nextElement())));
-            }
-        }
-
-        version = data.getVersion().getValue().intValue();
-
-        //
-        // Get the digest algorithm
-        //
-        digestalgos = new HashSet();
-        Enumeration e = data.getDigestAlgorithms().getObjects();
-
-        while (e.hasMoreElements())
-        {
-            ASN1Sequence s = (ASN1Sequence)e.nextElement();
-            DERObjectIdentifier o = (DERObjectIdentifier)s.getObjectAt(0);
-            digestalgos.add(o.getId());
-        }
-
-        //
-        // Get the SignerInfo
-        //
-        ASN1Set signerinfos = data.getSignerInfos();
-        if (signerinfos.size() != 1)
-        {
-            throw new SecurityException("This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
-        }
-
-        SignerInfo signerInfo = SignerInfo.getInstance(signerinfos.getObjectAt(0));
-
-        signerversion = signerInfo.getVersion().getValue().intValue();
-
-        IssuerAndSerialNumber isAnds = signerInfo.getIssuerAndSerialNumber();
-
-        //
-        // Get the signing certificate
-        //
-        BigInteger      serialNumber = isAnds.getCertificateSerialNumber().getValue();
-        X509Principal   issuer = new X509Principal(isAnds.getName());
-
-        for (Iterator i = certs.iterator();i.hasNext();)
-        {
-            X509Certificate cert = (X509Certificate)i.next();
-            if (serialNumber.equals(cert.getSerialNumber())
-                    && issuer.equals(cert.getIssuerDN()))
-            {
-                signCert = cert;
-                break;
-            }
-        }
-
-        if (signCert == null)
-        {
-            throw new SecurityException("Can't find signing certificate with serial "+serialNumber.toString(16)); 
-        }
-
-        digestAlgorithm = signerInfo.getDigestAlgorithm().getObjectId().getId();
-
-        digest = signerInfo.getEncryptedDigest().getOctets();
-        digestEncryptionAlgorithm = signerInfo.getDigestEncryptionAlgorithm().getObjectId().getId();
-
-        sig = Signature.getInstance(getDigestAlgorithm(), provider);
-
-        sig.initVerify(signCert.getPublicKey());
-    }
-
-    /**
-     * Create a new PKCS#7 object from the specified key using the BC provider.
-     *
-     * @param privKey the private key to be used for signing.
-     * @param certChain the certificate chain associated with the private key.
-     * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
-     */
-    public PKCS7SignedData(
-        PrivateKey      privKey,
-        Certificate[]   certChain,
-        String          hashAlgorithm)
-        throws SecurityException, InvalidKeyException,
-        NoSuchProviderException, NoSuchAlgorithmException
-    {
-        this(privKey, certChain, hashAlgorithm, "BC");
-    }
-
-    /**
-     * Create a new PKCS#7 object from the specified key.
-     *
-     * @param privKey the private key to be used for signing.
-     * @param certChain the certificate chain associated with the private key.
-     * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
-     * @param provider the provider to use.
-     */
-    public PKCS7SignedData(
-        PrivateKey      privKey,
-        Certificate[]   certChain,
-        String          hashAlgorithm,
-        String          provider)
-        throws SecurityException, InvalidKeyException,
-        NoSuchProviderException, NoSuchAlgorithmException
-    {
-        this(privKey, certChain, null, hashAlgorithm, provider);
-    }
-
-    /**
-     * Create a new PKCS#7 object from the specified key.
-     *
-     * @param privKey the private key to be used for signing.
-     * @param certChain the certificate chain associated with the private key.
-     * @param crlList the crl list associated with the private key.
-     * @param hashAlgorithm the hashing algorithm used to compute the message digest. Must be "MD5", "MD2", "SHA1" or "SHA"
-     * @param provider the provider to use.
-     */
-    public PKCS7SignedData(
-        PrivateKey      privKey,
-        Certificate[]   certChain,
-        CRL[]           crlList,
-        String          hashAlgorithm,
-        String          provider)
-        throws SecurityException, InvalidKeyException,
-        NoSuchProviderException, NoSuchAlgorithmException
-    {
-        this.privKey = privKey;
-
-        if (hashAlgorithm.equals("MD5"))
-        {
-            digestAlgorithm = ID_MD5;
-        }
-        else if (hashAlgorithm.equals("MD2"))
-        {
-            digestAlgorithm = ID_MD2;
-        }
-        else if (hashAlgorithm.equals("SHA"))
-        {
-            digestAlgorithm = ID_SHA1;
-        }
-        else if (hashAlgorithm.equals("SHA1"))
-        {
-            digestAlgorithm = ID_SHA1;
-        }
-        else
-        {
-            throw new NoSuchAlgorithmException("Unknown Hash Algorithm "+hashAlgorithm);
-        }
-
-        version = signerversion = 1;
-        certs = new ArrayList();
-        crls = new ArrayList();
-        digestalgos = new HashSet();
-        digestalgos.add(digestAlgorithm);
-
-        //
-        // Copy in the certificates and crls used to sign the private key.
-        //
-        signCert = (X509Certificate)certChain[0];
-        for (int i = 0;i < certChain.length;i++)
-        {
-            certs.add(certChain[i]);
-        }
-
-        if (crlList != null)
-        {
-            for (int i = 0;i < crlList.length;i++)
-            {
-                crls.add(crlList[i]);
-            }
-        }
-
-        //
-        // Now we have private key, find out what the digestEncryptionAlgorithm is.
-        //
-        digestEncryptionAlgorithm = privKey.getAlgorithm();
-        if (digestEncryptionAlgorithm.equals("RSA"))
-        {
-            digestEncryptionAlgorithm = ID_RSA;
-        }
-        else if (digestEncryptionAlgorithm.equals("DSA"))
-        {
-            digestEncryptionAlgorithm = ID_DSA;
-        }
-        else
-        {
-            throw new NoSuchAlgorithmException("Unknown Key Algorithm "+digestEncryptionAlgorithm);
-        }
-
-        sig = Signature.getInstance(getDigestAlgorithm(), provider);
-
-        sig.initSign(privKey);
-    }
-
-    /**
-     * Get the algorithm used to calculate the message digest
-     */
-    public String getDigestAlgorithm()
-    {
-        String da = digestAlgorithm;
-        String dea = digestEncryptionAlgorithm;
-
-        if (digestAlgorithm.equals(ID_MD5))
-        {
-            da = "MD5";
-        }
-        else if (digestAlgorithm.equals(ID_MD2))
-        {
-            da = "MD2";
-        }
-        else if (digestAlgorithm.equals(ID_SHA1))
-        {
-            da = "SHA1";
-        }
-
-        if (digestEncryptionAlgorithm.equals(ID_RSA))
-        {
-            dea = "RSA";
-        }
-        else if (digestEncryptionAlgorithm.equals(ID_DSA))
-        {
-            dea = "DSA";
-        }
-
-        return da + "with" + dea;
-    }
-
-    /**
-     * Resets the PKCS7SignedData object to it's initial state, ready
-     * to sign or verify a new buffer.
-     */
-    public void reset()
-    {
-        try
-        {
-            if (privKey==null)
-            {
-                sig.initVerify(signCert.getPublicKey());
-            }
-            else
-            {
-                sig.initSign(privKey);
-            }
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException(e.toString());
-        }
-    }
-
-    /**
-     * Get the X.509 certificates associated with this PKCS#7 object
-     */
-    public Certificate[] getCertificates()
-    {
-        return (X509Certificate[])certs.toArray(new X509Certificate[certs.size()]);
-    }
-
-    /**
-     * Get the X.509 certificate revocation lists associated with this PKCS#7 object
-     */
-    public Collection getCRLs()
-    {
-        return crls;
-    }
-    
-    /**
-     * Get the X.509 certificate actually used to sign the digest.
-     */
-    public X509Certificate getSigningCertificate()
-    {
-        return signCert;
-    }
-
-    /**
-     * Get the version of the PKCS#7 object. Always 1
-     */
-    public int getVersion()
-    {
-        return version;
-    }
-
-    /**
-     * Get the version of the PKCS#7 "SignerInfo" object. Always 1
-     */
-    public int getSigningInfoVersion()
-    {
-        return signerversion;
-    }
-
-    /**
-     * Update the digest with the specified byte. This method is used both for signing and verifying
-     */
-    public void update(byte buf)
-        throws SignatureException
-    {
-        sig.update(buf);
-    }
-
-    /**
-     * Update the digest with the specified bytes. This method is used both for signing and verifying
-     */
-    public void update(byte[] buf, int off, int len)
-        throws SignatureException
-    {
-        sig.update(buf, off, len);
-    }
-
-    /**
-     * Verify the digest
-     */
-    public boolean verify()
-        throws SignatureException
-    {
-        return sig.verify(digest);
-    }
-
-    /**
-     * Get the "issuer" from the TBSCertificate bytes that are passed in
-     */
-    private DERObject getIssuer(byte[] enc)
-    {
-        try
-        {
-            ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(enc));
-            ASN1Sequence seq = (ASN1Sequence)in.readObject();
-            return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3 : 2);
-        }
-        catch (IOException e)
-        {
-            throw new Error("IOException reading from ByteArray: "+e);
-        }
-    }
-
-    /**
-     * return the bytes for the PKCS7SignedData object.
-     */
-    public byte[] getEncoded()
-    {
-        try
-        {
-        
-            digest = sig.sign();
-
-            // Create the set of Hash algorithms. I've assumed this is the
-            // set of all hash agorithms used to created the digest in the
-            // "signerInfo" structure. I may be wrong.
-            //
-            ASN1EncodableVector v = new ASN1EncodableVector();
-            for (Iterator i = digestalgos.iterator(); i.hasNext();)
-            {
-                AlgorithmIdentifier a = new AlgorithmIdentifier(
-                            new DERObjectIdentifier((String)i.next()),
-                            null);
-                
-                v.add(a);
-            }
-
-            DERSet algos = new DERSet(v);
-
-            // Create the contentInfo. Empty, I didn't implement this bit
-            //
-            DERSequence contentinfo = new DERSequence(
-                                        new DERObjectIdentifier(ID_PKCS7_DATA));
-
-            // Get all the certificates
-            //
-            v = new ASN1EncodableVector();
-            for (Iterator i = certs.iterator();i.hasNext();)
-            {
-                ASN1InputStream tempstream = new ASN1InputStream(new ByteArrayInputStream(((X509Certificate)i.next()).getEncoded()));
-                v.add(tempstream.readObject());
-            }
-
-            DERSet dercertificates = new DERSet(v);
-
-            // Create signerinfo structure.
-            //
-            ASN1EncodableVector signerinfo = new ASN1EncodableVector();
-
-            // Add the signerInfo version
-            //
-            signerinfo.add(new DERInteger(signerversion));
-
-            IssuerAndSerialNumber isAnds = new IssuerAndSerialNumber(
-                        new X509Name((ASN1Sequence)getIssuer(signCert.getTBSCertificate())),
-                        new DERInteger(signCert.getSerialNumber()));
-            signerinfo.add(isAnds);
-
-            // Add the digestAlgorithm
-            //
-            // BEGIN android-changed
-            signerinfo.add(new AlgorithmIdentifier(
-                                new DERObjectIdentifier(digestAlgorithm),
-                                DERNull.THE_ONE));
-
-            //
-            // Add the digestEncryptionAlgorithm
-            //
-            signerinfo.add(new AlgorithmIdentifier(
-                                new DERObjectIdentifier(digestEncryptionAlgorithm),
-                                DERNull.THE_ONE));
-            // END android-changed
-
-            //
-            // Add the digest
-            //
-            signerinfo.add(new DEROctetString(digest));
-
-
-            //
-            // Finally build the body out of all the components above
-            //
-            ASN1EncodableVector body = new ASN1EncodableVector();
-            body.add(new DERInteger(version));
-            body.add(algos);
-            body.add(contentinfo);
-            body.add(new DERTaggedObject(false, 0, dercertificates));
-
-            if (crls.size()>0)
-            {
-                v = new ASN1EncodableVector();
-                for (Iterator i = crls.iterator();i.hasNext();)
-                {
-                    ASN1InputStream t = new ASN1InputStream(new ByteArrayInputStream(((X509CRL)i.next()).getEncoded()));
-                    v.add(t.readObject());
-                }
-                DERSet dercrls = new DERSet(v);
-                body.add(new DERTaggedObject(false, 1, dercrls));
-            }
-
-            // Only allow one signerInfo
-            //
-            body.add(new DERSet(new DERSequence(signerinfo)));
-
-            // Now we have the body, wrap it in it's PKCS7Signed shell
-            // and return it
-            //
-            ASN1EncodableVector whole = new ASN1EncodableVector();
-            whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA));
-            whole.add(new DERTaggedObject(0, new DERSequence(body)));
-
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-
-            DEROutputStream dout = new DEROutputStream(bOut);
-            dout.writeObject(new DERSequence(whole));
-            dout.close();
-
-            return bOut.toByteArray();
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException(e.toString());
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/PrincipalUtil.java b/src/main/java/org/bouncycastle/jce/PrincipalUtil.java
index 92a444c..6ccf1e4 100644
--- a/src/main/java/org/bouncycastle/jce/PrincipalUtil.java
+++ b/src/main/java/org/bouncycastle/jce/PrincipalUtil.java
@@ -24,11 +24,8 @@
     {
         try
         {
-            ByteArrayInputStream    bIn = new ByteArrayInputStream(
-                cert.getTBSCertificate());
-            ASN1InputStream         aIn = new ASN1InputStream(bIn);
-            TBSCertificateStructure tbsCert = new TBSCertificateStructure(
-                                            (ASN1Sequence)aIn.readObject());
+            TBSCertificateStructure tbsCert = TBSCertificateStructure.getInstance(
+                    ASN1Object.fromByteArray(cert.getTBSCertificate()));
 
             return new X509Principal(tbsCert.getIssuer());
         }
@@ -47,12 +44,8 @@
     {
         try
         {
-            ByteArrayInputStream    bIn = new ByteArrayInputStream(
-                cert.getTBSCertificate());
-            ASN1InputStream         aIn = new ASN1InputStream(bIn);
-            TBSCertificateStructure tbsCert = new TBSCertificateStructure(
-                                            (ASN1Sequence)aIn.readObject());
-
+            TBSCertificateStructure tbsCert = TBSCertificateStructure.getInstance(
+                    ASN1Object.fromByteArray(cert.getTBSCertificate()));
             return new X509Principal(tbsCert.getSubject());
         }
         catch (IOException e)
@@ -70,11 +63,8 @@
     {
         try
         {
-            ByteArrayInputStream    bIn = new ByteArrayInputStream(
-                crl.getTBSCertList());
-            ASN1InputStream         aIn = new ASN1InputStream(bIn);
-            TBSCertList tbsCertList = new TBSCertList(
-                                            (ASN1Sequence)aIn.readObject());
+            TBSCertList tbsCertList = TBSCertList.getInstance(
+                ASN1Object.fromByteArray(crl.getTBSCertList()));
 
             return new X509Principal(tbsCertList.getIssuer());
         }
diff --git a/src/main/java/org/bouncycastle/jce/ProviderConfigurationPermission.java b/src/main/java/org/bouncycastle/jce/ProviderConfigurationPermission.java
new file mode 100644
index 0000000..dba4db0
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/ProviderConfigurationPermission.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.jce;
+
+import org.bouncycastle.util.Strings;
+
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.util.StringTokenizer;
+
+/**
+ * A permission class to define what can be done with the ConfigurableProvider interface.
+ * <p>
+ * Available permissions are "threadLocalEcImplicitlyCa" and "ecImplicitlyCa" which allow the setting
+ * of the thread local and global ecImplicitlyCa parameters respectively.
+ * </p>
+ * <p>
+ * Examples:
+ * <ul>
+ * <li>ProviderConfigurationPermission("BC"); // enable all permissions</li>
+ * <li>ProviderConfigurationPermission("BC", "threadLocalEcImplicitlyCa"); // enable thread local only</li>
+ * <li>ProviderConfigurationPermission("BC", "ecImplicitlyCa"); // enable global setting only</li>
+ * <li>ProviderConfigurationPermission("BC", "threadLocalEcImplicitlyCa, ecImplicitlyCa"); // enable both explicitly</li>
+ * </ul>
+ * <p>
+ * Note: permission checks are only enforced if a security manager is present.
+ * </p>
+ */
+public class ProviderConfigurationPermission
+    extends BasicPermission
+{
+    private static final int  THREAD_LOCAL_EC_IMPLICITLY_CA = 0x01;
+
+    private static final int  EC_IMPLICITLY_CA = 0x02;
+    private static final int  ALL = THREAD_LOCAL_EC_IMPLICITLY_CA | EC_IMPLICITLY_CA;
+
+    private static final String THREAD_LOCAL_EC_IMPLICITLY_CA_STR = "threadlocalecimplicitlyca";
+    private static final String EC_IMPLICITLY_CA_STR = "ecimplicitlyca";
+    private static final String ALL_STR = "all";
+
+    private final String actions;
+    private final int permissionMask;
+
+    public ProviderConfigurationPermission(String name)
+    {
+        super(name);
+        this.actions = "all";
+        this.permissionMask = ALL;
+    }
+
+    public ProviderConfigurationPermission(String name, String actions)
+    {
+        super(name, actions);
+        this.actions = actions;
+        this.permissionMask = calculateMask(actions);
+    }
+
+    private int calculateMask(
+        String actions)
+    {
+        StringTokenizer tok = new StringTokenizer(Strings.toLowerCase(actions), " ,");
+        int             mask = 0;
+
+        while (tok.hasMoreTokens())
+        {
+            String s = tok.nextToken();
+
+            if (s.equals(THREAD_LOCAL_EC_IMPLICITLY_CA_STR))
+            {
+                mask |= THREAD_LOCAL_EC_IMPLICITLY_CA;
+            }
+            else if (s.equals(EC_IMPLICITLY_CA_STR))
+            {
+                mask |= EC_IMPLICITLY_CA;
+            }
+            else if (s.equals(ALL_STR))
+            {
+                mask |= ALL;
+            }
+        }
+
+        if (mask == 0)
+        {
+            throw new IllegalArgumentException("unknown permissions passed to mask");
+        }
+        
+        return mask;
+    }
+
+    public String getActions()
+    {
+        return actions;
+    }
+
+    public boolean implies(
+        Permission permission)
+    {
+        if (!(permission instanceof ProviderConfigurationPermission))
+        {
+            return false;
+        }
+
+        if (!this.getName().equals(permission.getName()))
+        {
+            return false;
+        }
+        
+        ProviderConfigurationPermission other = (ProviderConfigurationPermission)permission;
+        
+        return (this.permissionMask & other.permissionMask) == other.permissionMask;
+    }
+
+    public boolean equals(
+        Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (obj instanceof ProviderConfigurationPermission)
+        {
+            ProviderConfigurationPermission other = (ProviderConfigurationPermission)obj;
+
+            return this.permissionMask == other.permissionMask && this.getName().equals(other.getName());
+        }
+
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return this.getName().hashCode() + this.permissionMask;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/X509KeyUsage.java b/src/main/java/org/bouncycastle/jce/X509KeyUsage.java
deleted file mode 100644
index 2024b65..0000000
--- a/src/main/java/org/bouncycastle/jce/X509KeyUsage.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.bouncycastle.jce;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.x509.KeyUsage;
-
-/**
- * A holding class for constructing an X509 Key Usage extension.
- *
- * <pre>
- *    id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
- *
- *    KeyUsage ::= BIT STRING {
- *         digitalSignature        (0),
- *         nonRepudiation          (1),
- *         keyEncipherment         (2),
- *         dataEncipherment        (3),
- *         keyAgreement            (4),
- *         keyCertSign             (5),
- *         cRLSign                 (6),
- *         encipherOnly            (7),
- *         decipherOnly            (8) }
- * </pre>
- */
-public class X509KeyUsage
-    extends ASN1Encodable
-{
-    public static final int        digitalSignature = 1 << 7; 
-    public static final int        nonRepudiation   = 1 << 6;
-    public static final int        keyEncipherment  = 1 << 5;
-    public static final int        dataEncipherment = 1 << 4;
-    public static final int        keyAgreement     = 1 << 3;
-    public static final int        keyCertSign      = 1 << 2;
-    public static final int        cRLSign          = 1 << 1;
-    public static final int        encipherOnly     = 1 << 0;
-    public static final int        decipherOnly     = 1 << 15;
-
-    private int usage = 0;
-
-    /**
-     * Basic constructor.
-     * 
-     * @param usage - the bitwise OR of the Key Usage flags giving the
-     * allowed uses for the key.
-     * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment)
-     */
-    public X509KeyUsage(
-        int usage)
-    {
-        this.usage = usage;
-    }
-
-    public DERObject toASN1Object()
-    {
-        return new KeyUsage(usage);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/X509Principal.java b/src/main/java/org/bouncycastle/jce/X509Principal.java
index 7051073..1d867e7 100644
--- a/src/main/java/org/bouncycastle/jce/X509Principal.java
+++ b/src/main/java/org/bouncycastle/jce/X509Principal.java
@@ -1,16 +1,24 @@
 package org.bouncycastle.jce;
 
-import java.io.ByteArrayOutputStream;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x509.X509Name;
+
 import java.io.IOException;
 import java.security.Principal;
 import java.util.Hashtable;
 import java.util.Vector;
 
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.x509.X509Name;
-
+/**
+ * a general extension of X509Name with a couple of extra methods and
+ * constructors.
+ * <p>
+ * Objects of this type can be created from certificates and CRLs using the
+ * PrincipalUtil class.
+ * </p>
+ * @see org.bouncycastle.jce.PrincipalUtil
+ */
 public class X509Principal
     extends X509Name
     implements Principal
@@ -134,18 +142,13 @@
      */
     public byte[] getEncoded()
     {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(this);
+            return this.getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
             throw new RuntimeException(e.toString());
         }
-
-        return bOut.toByteArray();
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/X509V1CertificateGenerator.java b/src/main/java/org/bouncycastle/jce/X509V1CertificateGenerator.java
deleted file mode 100644
index 4bd4c77..0000000
--- a/src/main/java/org/bouncycastle/jce/X509V1CertificateGenerator.java
+++ /dev/null
@@ -1,265 +0,0 @@
-package org.bouncycastle.jce;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Hashtable;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.TBSCertificateStructure;
-import org.bouncycastle.asn1.x509.Time;
-import org.bouncycastle.asn1.x509.V1TBSCertificateGenerator;
-import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-import org.bouncycastle.util.Strings;
-
-/**
- * class to produce an X.509 Version 1 certificate.
- * 
- * @deprecated use the equivalent class in org.bouncycastle.x509
- */
-public class X509V1CertificateGenerator
-{
-    private V1TBSCertificateGenerator   tbsGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-
-    private static Hashtable            algorithms = new Hashtable();
-
-    static
-    {
-        algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("SHA1WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("SHA1WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("SHA1WITHDSA", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("DSAWITHSHA1", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("SHA1WITHECDSA", new DERObjectIdentifier("1.2.840.10045.4.1"));
-        algorithms.put("ECDSAWITHSHA1", new DERObjectIdentifier("1.2.840.10045.4.1"));
-    }
-
-    public X509V1CertificateGenerator()
-    {
-        tbsGen = new V1TBSCertificateGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        tbsGen = new V1TBSCertificateGenerator();
-    }
-
-    /**
-     * set the serial number for the certificate.
-     */
-    public void setSerialNumber(
-        BigInteger      serialNumber)
-    {
-        tbsGen.setSerialNumber(new DERInteger(serialNumber));
-    }
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X509Name   issuer)
-    {
-        tbsGen.setIssuer(issuer);
-    }
-
-    public void setNotBefore(
-        Date    date)
-    {
-        tbsGen.setStartDate(new Time(date));
-    }
-
-    public void setNotAfter(
-        Date    date)
-    {
-        tbsGen.setEndDate(new Time(date));
-    }
-
-    /**
-     * Set the subject distinguished name. The subject describes the entity associated with the public key.
-     */
-    public void setSubjectDN(
-        X509Name   subject)
-    {
-        tbsGen.setSubject(subject);
-    }
-
-    public void setPublicKey(
-        PublicKey       key)
-    {
-        try
-        {
-            tbsGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
-                                new ByteArrayInputStream(key.getEncoded())).readObject()));
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("unable to process key - " + e.toString());
-        }
-    }
-
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        sigOID = (DERObjectIdentifier)algorithms.get(Strings.toUpperCase(signatureAlgorithm));
-
-        if (sigOID == null)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        // BEGIN android-changed
-        sigAlgId = new AlgorithmIdentifier(this.sigOID, DERNull.THE_ONE);
-        // END android-changed
-
-        tbsGen.setSignature(sigAlgId);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC".
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", null);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC" and the passed in source of randomness
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        SecureRandom    random)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", random);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing, and the passed in source
-     * of randomness (if required).
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateX509Certificate(key, provider, null);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing, and the passed in source
-     * of randomness (if required).
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        TBSCertificateStructure tbsCert = tbsGen.generateTBSCertificate();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCert);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding TBS cert - " + e);
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(tbsCert);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        return new X509CertificateObject(new X509CertificateStructure(new DERSequence(v)));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/X509V2CRLGenerator.java b/src/main/java/org/bouncycastle/jce/X509V2CRLGenerator.java
deleted file mode 100644
index dea70e2..0000000
--- a/src/main/java/org/bouncycastle/jce/X509V2CRLGenerator.java
+++ /dev/null
@@ -1,331 +0,0 @@
-package org.bouncycastle.jce;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CRLException;
-import java.security.cert.X509CRL;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.SimpleTimeZone;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERUTCTime;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.CertificateList;
-import org.bouncycastle.asn1.x509.TBSCertList;
-import org.bouncycastle.asn1.x509.V2TBSCertListGenerator;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.provider.X509CRLObject;
-import org.bouncycastle.util.Strings;
-
-/**
- * class to produce an X.509 Version 2 CRL.
- * <p>
- * @deprecated use the equivalent class in org.bouncycastle.x509
- */
-public class X509V2CRLGenerator
-{
-    private SimpleDateFormat            dateF = new SimpleDateFormat("yyMMddHHmmss");
-    private SimpleTimeZone              tz = new SimpleTimeZone(0, "Z");
-    private V2TBSCertListGenerator      tbsGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-    private Hashtable                   extensions = null;
-    private Vector                      extOrdering = null;
-
-    private static Hashtable            algorithms = new Hashtable();
-
-    static
-    {
-        algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("SHA1WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("SHA1WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("SHA1WITHDSA", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("DSAWITHSHA1", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("SHA1WITHECDSA", new DERObjectIdentifier("1.2.840.10045.4.1"));
-        algorithms.put("ECDSAWITHSHA1", new DERObjectIdentifier("1.2.840.10045.4.1"));
-    }
-
-    public X509V2CRLGenerator()
-    {
-        dateF.setTimeZone(tz);
-
-        tbsGen = new V2TBSCertListGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        tbsGen = new V2TBSCertListGenerator();
-    }
-
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X509Name   issuer)
-    {
-        tbsGen.setIssuer(issuer);
-    }
-
-    public void setThisUpdate(
-        Date    date)
-    {
-        tbsGen.setThisUpdate(new DERUTCTime(dateF.format(date) + "Z"));
-    }
-
-    public void setNextUpdate(
-        Date    date)
-    {
-        tbsGen.setNextUpdate(new DERUTCTime(dateF.format(date) + "Z"));
-    }
-
-    /**
-     * Reason being as indicated by ReasonFlags, i.e. ReasonFlags.KEY_COMPROMISE
-     * or 0 if ReasonFlags are not to be used
-     **/
-    public void addCRLEntry(BigInteger userCertificate, Date revocationDate, int reason)
-    {
-        tbsGen.addCRLEntry(new DERInteger(userCertificate), new DERUTCTime(dateF.format(revocationDate) + "Z"), reason);
-    }
-
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        sigOID = (DERObjectIdentifier)algorithms.get(Strings.toUpperCase(signatureAlgorithm));
-
-        if (sigOID == null)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        sigAlgId = new AlgorithmIdentifier(this.sigOID, null);
-
-        tbsGen.setSignature(sigAlgId);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 3)
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        DEREncodable    value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        DEREncodable        value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
-        try
-        {
-            dOut.writeObject(value);
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("error encoding value: " + e);
-        }
-
-        this.addExtension(OID, critical, bOut.toByteArray());
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        byte[]          value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        byte[]              value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        extensions.put(OID, new X509Extension(critical, new DEROctetString(value)));
-        extOrdering.addElement(OID);
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject
-     * using the default provider "BC".
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509CRL(key, "BC", null);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject
-     * using the default provider "BC" and an user defined SecureRandom object as
-     * source of randomness.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        SecureRandom    random)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509CRL(key, "BC", random);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the passed in provider for the signing.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateX509CRL(key, provider, null);
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject,
-     * using the passed in provider for the signing.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        if (extensions != null)
-        {
-            tbsGen.setExtensions(new X509Extensions(extOrdering, extensions));
-        }
-
-        TBSCertList tbsCrl = tbsGen.generateTBSCertList();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCrl);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding TBS cert - " + e);
-        }
-
-        // Construct the CRL
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(tbsCrl);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        try
-        {
-            return new X509CRLObject(new CertificateList(new DERSequence(v)));
-        }
-        catch (CRLException e)
-        {
-            throw new IllegalStateException("attempt to create malformed CRL: " + e.getMessage());
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/X509V3CertificateGenerator.java b/src/main/java/org/bouncycastle/jce/X509V3CertificateGenerator.java
deleted file mode 100644
index aec0365..0000000
--- a/src/main/java/org/bouncycastle/jce/X509V3CertificateGenerator.java
+++ /dev/null
@@ -1,345 +0,0 @@
-package org.bouncycastle.jce;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.*;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.TBSCertificateStructure;
-import org.bouncycastle.asn1.x509.Time;
-import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-import org.bouncycastle.util.Strings;
-
-/**
- * class to produce an X.509 Version 3 certificate.
- * @deprecated use the equivalent class in org.bouncycastle.x509
- */
-public class X509V3CertificateGenerator
-{
-    private V3TBSCertificateGenerator   tbsGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-    private Hashtable                   extensions = null;
-    private Vector                      extOrdering = null;
-
-    private static Hashtable            algorithms = new Hashtable();
-
-    static
-    {
-        algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("SHA1WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("SHA1WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("SHA1WITHDSA", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("DSAWITHSHA1", new DERObjectIdentifier("1.2.840.10040.4.3"));
-        algorithms.put("SHA1WITHECDSA", new DERObjectIdentifier("1.2.840.10045.4.1"));
-        algorithms.put("ECDSAWITHSHA1", new DERObjectIdentifier("1.2.840.10045.4.1"));
-    }
-
-    public X509V3CertificateGenerator()
-    {
-        tbsGen = new V3TBSCertificateGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        tbsGen = new V3TBSCertificateGenerator();
-        extensions = null;
-        extOrdering = null;
-    }
-
-    /**
-     * set the serial number for the certificate.
-     */
-    public void setSerialNumber(
-        BigInteger      serialNumber)
-    {
-        tbsGen.setSerialNumber(new DERInteger(serialNumber));
-    }
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X509Name   issuer)
-    {
-        tbsGen.setIssuer(issuer);
-    }
-
-    public void setNotBefore(
-        Date    date)
-    {
-        tbsGen.setStartDate(new Time(date));
-    }
-
-    public void setNotAfter(
-        Date    date)
-    {
-        tbsGen.setEndDate(new Time(date));
-    }
-
-    /**
-     * Set the subject distinguished name. The subject describes the entity associated with the public key.
-     */
-    public void setSubjectDN(
-        X509Name   subject)
-    {
-        tbsGen.setSubject(subject);
-    }
-
-    public void setPublicKey(
-        PublicKey       key)
-    {
-        try
-        {
-            tbsGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
-                                new ByteArrayInputStream(key.getEncoded())).readObject()));
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("unable to process key - " + e.toString());
-        }
-    }
-
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        sigOID = (DERObjectIdentifier)algorithms.get(Strings.toUpperCase(signatureAlgorithm));
-
-        if (sigOID == null)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        // BEGIN android-changed
-        sigAlgId = new AlgorithmIdentifier(this.sigOID, DERNull.THE_ONE);
-        // END android-changed
-
-        tbsGen.setSignature(sigAlgId);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 3)
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        DEREncodable    value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 3)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        DEREncodable        value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
-        try
-        {
-            dOut.writeObject(value);
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("error encoding value: " + e);
-        }
-
-        this.addExtension(OID, critical, bOut.toByteArray());
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 3)
-     * The value parameter becomes the contents of the octet string associated
-     * with the extension.
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        byte[]          value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 3)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        byte[]              value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        extensions.put(OID, new X509Extension(critical, new DEROctetString(value)));
-        extOrdering.addElement(OID);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC".
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", null);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC", and the passed in source of randomness
-     * (if required).
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        SecureRandom    random)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", random);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing.
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateX509Certificate(key, provider, null);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing and the supplied source
-     * of randomness, if required.
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        if (sigOID == null)
-        {
-            throw new IllegalStateException("no signature algorithm specified");
-        }
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        if (extensions != null)
-        {
-            tbsGen.setExtensions(new X509Extensions(extOrdering, extensions));
-        }
-
-        TBSCertificateStructure tbsCert = tbsGen.generateTBSCertificate();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCert);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding TBS cert - " + e);
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(tbsCert);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        return new X509CertificateObject(new X509CertificateStructure(new DERSequence(v)));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/exception/ExtCertPathBuilderException.java b/src/main/java/org/bouncycastle/jce/exception/ExtCertPathBuilderException.java
new file mode 100644
index 0000000..a0b2d90
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/exception/ExtCertPathBuilderException.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.jce.exception;
+
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilderException;
+
+public class ExtCertPathBuilderException
+    extends CertPathBuilderException
+    implements ExtException
+{
+    private Throwable cause;
+
+    public ExtCertPathBuilderException(String message, Throwable cause)
+    {
+        super(message);
+        this.cause = cause;
+    }
+
+    public ExtCertPathBuilderException(String msg, Throwable cause, 
+        CertPath certPath, int index)
+    {
+        super(msg, cause);
+        this.cause = cause;
+    }
+    
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/exception/ExtCertPathValidatorException.java b/src/main/java/org/bouncycastle/jce/exception/ExtCertPathValidatorException.java
new file mode 100644
index 0000000..e36848f
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/exception/ExtCertPathValidatorException.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.jce.exception;
+
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidatorException;
+
+public class ExtCertPathValidatorException
+    extends CertPathValidatorException
+    implements ExtException
+{
+
+    private Throwable cause;
+
+    public ExtCertPathValidatorException(String message, Throwable cause)
+    {
+        super(message);
+        this.cause = cause;
+    }
+
+    public ExtCertPathValidatorException(String msg, Throwable cause, 
+        CertPath certPath, int index)
+    {
+        super(msg, cause, certPath, index);
+        this.cause = cause;
+    }
+    
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/exception/ExtException.java b/src/main/java/org/bouncycastle/jce/exception/ExtException.java
new file mode 100644
index 0000000..52c60de
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/exception/ExtException.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.jce.exception;
+
+/**
+ * 
+ * This is an extended exception. Java before version 1.4 did not offer the
+ * possibility the attach a cause to an exception. The cause of an exception is
+ * the <code>Throwable</code> object which was thrown and caused the
+ * exception. This interface must be implemented by all exceptions to accomplish
+ * this additional functionality.
+ * 
+ */
+public interface ExtException
+{
+
+    /**
+     * Returns the cause of the exception.
+     * 
+     * @return The cause of the exception.
+     */
+    Throwable getCause();
+}
diff --git a/src/main/java/org/bouncycastle/jce/interfaces/ConfigurableProvider.java b/src/main/java/org/bouncycastle/jce/interfaces/ConfigurableProvider.java
new file mode 100644
index 0000000..5aa2d9c
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/interfaces/ConfigurableProvider.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.jce.interfaces;
+
+/**
+ * Implemented by the BC provider. This allows setting of hidden parameters,
+ * such as the ImplicitCA parameters from X.962, if used.
+ */
+public interface ConfigurableProvider
+{
+    static final String      THREAD_LOCAL_EC_IMPLICITLY_CA = "threadLocalEcImplicitlyCa";   
+    static final String      EC_IMPLICITLY_CA = "ecImplicitlyCa";
+
+    void setParameter(String parameterName, Object parameter);
+}
diff --git a/src/main/java/org/bouncycastle/jce/interfaces/IESKey.java b/src/main/java/org/bouncycastle/jce/interfaces/IESKey.java
deleted file mode 100644
index f1d7901..0000000
--- a/src/main/java/org/bouncycastle/jce/interfaces/IESKey.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.bouncycastle.jce.interfaces;
-
-import java.security.Key;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-/**
- * key pair for use with an integrated encryptor
- */
-public interface IESKey
-    extends Key
-{
-    /**
-     * return the intended recipient's/sender's public key.
-     */
-    public PublicKey getPublic();
-
-    /**
-     * return the local private key.
-     */
-    public PrivateKey getPrivate();
-}
diff --git a/src/main/java/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java b/src/main/java/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java
index c5dd664..f27652e 100644
--- a/src/main/java/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java
+++ b/src/main/java/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java
@@ -10,12 +10,12 @@
  */
 public interface PKCS12BagAttributeCarrier
 {
-    public void setBagAttribute(
+    void setBagAttribute(
         DERObjectIdentifier oid,
         DEREncodable        attribute);
 
-    public DEREncodable getBagAttribute(
+    DEREncodable getBagAttribute(
         DERObjectIdentifier oid);
 
-    public Enumeration getBagAttributeKeys();
+    Enumeration getBagAttributeKeys();
 }
diff --git a/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java b/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
index ca3d8ea..e34d7ed 100644
--- a/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
+++ b/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
@@ -230,9 +230,6 @@
             sig.initSign(priv_key);
         }
 
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DEROutputStream deros = new DEROutputStream(baos);
-
         ASN1EncodableVector pkac = new ASN1EncodableVector();
 
         pkac.add(getKeySpec());
@@ -240,16 +237,13 @@
 
         try
         {
-            deros.writeObject(new DERSequence(pkac));
-            deros.close();
+            sig.update(new DERSequence(pkac).getEncoded(ASN1Encodable.DER));
         }
         catch (IOException ioe)
         {
             throw new SignatureException(ioe.getMessage());
         }
 
-        sig.update(baos.toByteArray());
-
         sigBits = sig.sign();
     }
 
diff --git a/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java b/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java
index e68139a..c9ac46e 100644
--- a/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java
+++ b/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java
@@ -1,30 +1,30 @@
 package org.bouncycastle.jce.provider;
 
-public class AnnotatedException 
-    extends Exception
-{
-    private Exception _underlyingException;
+import org.bouncycastle.jce.exception.ExtException;
 
-    AnnotatedException(
-        String string, 
-        Exception e)
+public class AnnotatedException
+    extends Exception
+    implements ExtException
+{
+    private Throwable _underlyingException;
+
+    AnnotatedException(String string, Throwable e)
     {
         super(string);
-        
+
         _underlyingException = e;
     }
-    
-    AnnotatedException(
-        String string)
+
+    AnnotatedException(String string)
     {
         this(string, null);
     }
 
-    Exception getUnderlyingException()
+    Throwable getUnderlyingException()
     {
         return _underlyingException;
     }
-    
+
     public Throwable getCause()
     {
         return _underlyingException;
diff --git a/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 4ec32a9..25735f4 100644
--- a/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -1,15 +1,20 @@
 package org.bouncycastle.jce.provider;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.security.Provider;
+import java.util.Iterator;
+import java.util.Map;
 
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
+import org.bouncycastle.jce.interfaces.ConfigurableProvider;
 
 /**
  * To add the provider at runtime use:
@@ -31,16 +36,42 @@
  * </code>
  * </pre>
  * Where &lt;n&gt; is the preference you want the provider at (1 being the
- * most prefered).
- * <p>Note: JCE algorithm names should be uppercase only so the case insensitive
+ * most preferred).
+ * <p>Note: JCE algorithm names should be upper-case only so the case insensitive
  * test for getInstance works.
  */
 public final class BouncyCastleProvider extends Provider
+    implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.34";
+    private static String info = "BouncyCastle Security Provider v1.45";
 
     public static String PROVIDER_NAME = "BC";
 
+    /*
+     * Configurable symmetric ciphers
+     */
+    private static final String SYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jce.provider.symmetric.";
+    private static final String[] SYMMETRIC_CIPHERS =
+    {
+        // BEGIN android-removed
+        // "AES", "Camellia", "CAST5", "Grainv1", "Grain128", "IDEA", "Noekeon", "SEED"
+        // END android-removed
+        // BEGIN android-added
+        "AES",
+        // END android-added
+    };
+
+    /*
+     * Configurable asymmetric ciphers
+     */
+    private static final String ASYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jce.provider.asymmetric.";
+    private static final String[] ASYMMETRIC_CIPHERS =
+    {
+        // BEGIN android-removed
+        // "EC"
+        // END android-removed
+    };
+
     /**
      * Construct a new provider.  This should only be required when
      * using runtime registration of the provider using the
@@ -48,7 +79,46 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.34, info);
+        super(PROVIDER_NAME, 1.45, info);
+
+        AccessController.doPrivileged(new PrivilegedAction()
+        {
+            public Object run()
+            {
+                setup();
+                return null;
+            }
+        });
+    }
+
+    private void setup()
+    {
+        loadAlgorithms(SYMMETRIC_CIPHER_PACKAGE, SYMMETRIC_CIPHERS);
+        loadAlgorithms(ASYMMETRIC_CIPHER_PACKAGE, ASYMMETRIC_CIPHERS);
+
+        // BEGIN android-removed
+        // //
+        // // X509Store
+        // //
+        // put("X509Store.CERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertCollection");
+        // put("X509Store.ATTRIBUTECERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreAttrCertCollection");
+        // put("X509Store.CRL/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCRLCollection");
+        // put("X509Store.CERTIFICATEPAIR/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertPairCollection");
+        //
+        // put("X509Store.CERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCerts");
+        // put("X509Store.CRL/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCRLs");
+        // put("X509Store.ATTRIBUTECERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPAttrCerts");
+        // put("X509Store.CERTIFICATEPAIR/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCertPairs");
+        //
+        // //
+        // // X509StreamParser
+        // //
+        // put("X509StreamParser.CERTIFICATE", "org.bouncycastle.jce.provider.X509CertParser");
+        // put("X509StreamParser.ATTRIBUTECERTIFICATE", "org.bouncycastle.jce.provider.X509AttrCertParser");
+        // put("X509StreamParser.CRL", "org.bouncycastle.jce.provider.X509CRLParser");
+        // put("X509StreamParser.CERTIFICATEPAIR", "org.bouncycastle.jce.provider.X509CertPairParser");
+        // END android-removed
+
 
         //
         // KeyStore
@@ -56,8 +126,25 @@
         put("KeyStore.BKS", "org.bouncycastle.jce.provider.JDKKeyStore");
         put("KeyStore.BouncyCastle", "org.bouncycastle.jce.provider.JDKKeyStore$BouncyCastleStore");
         put("KeyStore.PKCS12", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
-        put("KeyStore.BCPKCS12", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
-        put("KeyStore.PKCS12-DEF", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+        // BEGIN android-changed
+        put("Alg.Alias.KeyStore.BCPKCS12", "PKCS12");
+        // END android-changed
+        // BEGIN android-removed
+        // put("KeyStore.PKCS12-DEF", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+        // END android-removed
+
+        // BEGIN android-changed
+        put("Alg.Alias.KeyStore.PKCS12-3DES-40RC2", "PKCS12");
+        // END android-changed
+        // BEGIN android-removed
+        // put("KeyStore.PKCS12-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore3DES");
+        // END android-removed
+
+        // BEGIN android-removed
+        // put("KeyStore.PKCS12-DEF-3DES-40RC2", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+        // put("KeyStore.PKCS12-DEF-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore3DES");
+        // END android-removed
+
         put("Alg.Alias.KeyStore.UBER", "BouncyCastle");
         put("Alg.Alias.KeyStore.BOUNCYCASTLE", "BouncyCastle");
         put("Alg.Alias.KeyStore.bouncycastle", "BouncyCastle");
@@ -76,133 +163,143 @@
         // BEGIN android-removed
         // put("AlgorithmParameterGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$GOST3410");
         // put("AlgorithmParameterGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$ElGamal");
-        // END android-removed
-        put("AlgorithmParameterGenerator.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
-        put("AlgorithmParameterGenerator.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
-        put("AlgorithmParameterGenerator.1.2.840.113549.3.7", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
-        put("AlgorithmParameterGenerator.1.3.14.3.2.7", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
-        // BEGIN android-removed
-        // put("AlgorithmParameterGenerator.IDEA", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$IDEA");
-        // put("AlgorithmParameterGenerator.1.3.6.1.4.1.188.7.1.1.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$IDEA");
+        // put("AlgorithmParameterGenerator.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+        // put("AlgorithmParameterGenerator.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+        // put("AlgorithmParameterGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
+        // put("AlgorithmParameterGenerator." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$DES");
         // put("AlgorithmParameterGenerator.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
         // put("AlgorithmParameterGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$RC2");
-        // put("AlgorithmParameterGenerator.CAST5", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$CAST5");
-        // put("AlgorithmParameterGenerator.1.2.840.113533.7.66.10", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$CAST5");
         // END android-removed
-        put("AlgorithmParameterGenerator.AES", "org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator$AES");
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.2", "AES");  // these first 3 are wrong, but seem to have got around
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.22", "AES");
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.42", "AES");
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.1.2", "AES");
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.1.22", "AES");
-        put("Alg.Alias.AlgorithmParameterGenerator.2.16.840.1.101.3.4.1.42", "AES");
+
+        put("Alg.Alias.AlgorithmParameterGenerator.DIFFIEHELLMAN", "DH");
         // BEGIN android-removed
         // put("Alg.Alias.AlgorithmParameterGenerator.GOST-3410", "GOST3410");
         // END android-removed
-
         //
         // algorithm parameters
         //
         put("AlgorithmParameters.OAEP", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$OAEP");
-        put("AlgorithmParameters.PSS", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PSS");
+        // BEGIN android-removed
+        // put("AlgorithmParameters.PSS", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PSS");
+        // END android-removed
         put("AlgorithmParameters.DH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$DH");
+        put("Alg.Alias.AlgorithmParameters.DIFFIEHELLMAN", "DH");
         put("AlgorithmParameters.DSA", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$DSA");
         // BEGIN android-removed
         // put("AlgorithmParameters.ELGAMAL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$ElGamal");
+        // put("AlgorithmParameters.IES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IES");
         // END android-removed
-        put("AlgorithmParameters.IES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IES");
         put("AlgorithmParameters.PKCS12PBE", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PKCS12PBE");
+        // BEGIN android-changed
+        // redundant with below
+        // put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
+        // END android-changed
         // BEGIN android-removed
-        // double entry
-        // put("AlgorithmParameters.1.2.840.113549.3.7", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
-        // END android-removed
-        // BEGIN android-removed
-        // put("AlgorithmParameters.IDEA", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IDEAAlgorithmParameters");
-        // put("AlgorithmParameters.1.3.6.1.4.1.188.7.1.1.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IDEAAlgorithmParameters");
-        // put("AlgorithmParameters.CAST5", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$CAST5AlgorithmParameters");
-        // put("AlgorithmParameters.1.2.840.113533.7.66.10", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$CAST5AlgorithmParameters");
+        // put("AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PBKDF2");
+        //
         // put("AlgorithmParameters.GOST3410", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$GOST3410");
         // put("Alg.Alias.AlgorithmParameters.GOST-3410", "GOST3410");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC2", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC4", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2-CBC", "PKCS12PBE");
         // END android-removed
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC2", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC4", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDIDEA", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2-CBC", "PKCS12PBE");
         put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC2-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC4", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC2-CBC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC4", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDIDEA", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.1", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.2", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.3", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.4", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.5", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.6", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWithSHAAnd3KeyTripleDES", "PKCS12PBE");
+        // BEGIN android-removed
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC2-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC4", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC2-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC4", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.1", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.2", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.3", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.4", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.5", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.6", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWithSHAAnd3KeyTripleDES", "PKCS12PBE");
+        //
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
+        // END android-removed
+
         put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP");
         
-        put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
-        put("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
-        put("Alg.Alias.AlgorithmParameters.SHA224WITHRSAANDMGF1", "PSS");
-        put("Alg.Alias.AlgorithmParameters.SHA256WITHRSAANDMGF1", "PSS");
-        put("Alg.Alias.AlgorithmParameters.SHA384WITHRSAANDMGF1", "PSS");
-        put("Alg.Alias.AlgorithmParameters.SHA512WITHRSAANDMGF1", "PSS");
-        
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
-        put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE");
-
         // BEGIN android-removed
+        // put("Alg.Alias.AlgorithmParameters.RSAPSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.RSASSA-PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA224withRSA/PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA256withRSA/PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA384withRSA/PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA512withRSA/PSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA224WITHRSAANDMGF1", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA256WITHRSAANDMGF1", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA384WITHRSAANDMGF1", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.SHA512WITHRSAANDMGF1", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.RAWRSAPSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.NONEWITHRSAPSS", "PSS");
+        // put("Alg.Alias.AlgorithmParameters.NONEWITHRSASSA-PSS", "PSS");
+        //
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
+        // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE");
+        //
         // put("AlgorithmParameters.SHA1WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
         // put("AlgorithmParameters.SHA224WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
         // put("AlgorithmParameters.SHA256WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
         // put("AlgorithmParameters.SHA384WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
         // put("AlgorithmParameters.SHA512WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
         // END android-removed
-
+        
         //
         // key agreement
         //
         put("KeyAgreement.DH", "org.bouncycastle.jce.provider.JCEDHKeyAgreement");
-        // BEGIN android-removed
-        // put("KeyAgreement.ECDH", "org.bouncycastle.jce.provider.JCEECDHKeyAgreement$DH");
-        // put("KeyAgreement.ECDHC", "org.bouncycastle.jce.provider.JCEECDHKeyAgreement$DHC");
-        // END android-removed
-
+        put("Alg.Alias.KeyAgreement.DIFFIEHELLMAN", "DH");
+        
         //
         // cipher engines
         //
         put("Cipher.DES", "org.bouncycastle.jce.provider.JCEBlockCipher$DES");
         put("Cipher.DESEDE", "org.bouncycastle.jce.provider.JCEBlockCipher$DESede");
-        put("Cipher.1.2.840.113549.3.7", "org.bouncycastle.jce.provider.JCEBlockCipher$DESedeCBC");
-        put("Cipher.1.3.14.3.2.7", "org.bouncycastle.jce.provider.JCEBlockCipher$DESCBC");
+        // BEGIN android-removed
+        // put("Cipher." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESedeCBC");
+        // put("Cipher." + OIWObjectIdentifiers.desCBC, "org.bouncycastle.jce.provider.JCEBlockCipher$DESCBC");
+        // END android-removed
         put("Cipher.DESEDEWRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$DESEDEWrap");
-        put("Cipher.1.2.840.113549.1.9.16.3.6", "org.bouncycastle.jce.provider.WrapCipherSpi$DESEDEWrap");
+        // BEGIN android-changed
+        put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWRAP");
+        // END android-changed
         // BEGIN android-removed
         // put("Cipher.SKIPJACK", "org.bouncycastle.jce.provider.JCEBlockCipher$Skipjack");
         // put("Cipher.BLOWFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Blowfish");
+        // put("Cipher.1.3.6.1.4.1.3029.1.2", "org.bouncycastle.jce.provider.JCEBlockCipher$BlowfishCBC");
         // put("Cipher.TWOFISH", "org.bouncycastle.jce.provider.JCEBlockCipher$Twofish");
         // put("Cipher.RC2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2");
         // put("Cipher.RC2WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RC2Wrap");
@@ -211,109 +308,68 @@
         // put("Alg.Alias.Cipher.1.2.840.113549.3.4", "ARC4");
         // put("Alg.Alias.Cipher.ARCFOUR", "ARC4");
         // put("Alg.Alias.Cipher.RC4", "ARC4");
+        // put("Cipher.SALSA20", "org.bouncycastle.jce.provider.JCEStreamCipher$Salsa20");
+        // put("Cipher.HC128", "org.bouncycastle.jce.provider.JCEStreamCipher$HC128");
+        // put("Cipher.HC256", "org.bouncycastle.jce.provider.JCEStreamCipher$HC256");
+        // put("Cipher.VMPC", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPC");
+        // put("Cipher.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEStreamCipher$VMPCKSA3");
         // put("Cipher.RC5", "org.bouncycastle.jce.provider.JCEBlockCipher$RC5");
         // put("Cipher.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEBlockCipher$RC2CBC");
         // put("Alg.Alias.Cipher.RC5-32", "RC5");
         // put("Cipher.RC5-64", "org.bouncycastle.jce.provider.JCEBlockCipher$RC564");
         // put("Cipher.RC6", "org.bouncycastle.jce.provider.JCEBlockCipher$RC6");
-        // AES uses some functionality from Rijdael perhaps ...  
         // put("Cipher.RIJNDAEL", "org.bouncycastle.jce.provider.JCEBlockCipher$Rijndael");
-        // END android-removed
-        put("Cipher.AES", "org.bouncycastle.jce.provider.JCEBlockCipher$AES");
-        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.2", "AES");
-        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.22", "AES");
-        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.42", "AES");
-        put("Cipher." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.JCEBlockCipher$AES");
-        put("Cipher." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.JCEBlockCipher$AES");
-        put("Cipher." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.JCEBlockCipher$AES");
-        put("Cipher." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCBC");
-        put("Cipher." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCBC");
-        put("Cipher." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCBC");
-        put("Cipher." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESOFB");
-        put("Cipher." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESOFB");
-        put("Cipher." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESOFB");
-        put("Cipher." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCFB");
-        put("Cipher." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCFB");
-        put("Cipher." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.JCEBlockCipher$AESCFB");
-        put("Cipher.AESWRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$AESWrap");
-        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_wrap, "AESWRAP");
-        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_wrap, "AESWRAP");
-        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_wrap, "AESWRAP");
-        
-        // BEGIN android-removed
+        // put("Cipher.DESEDERFC3211WRAP", "org.bouncycastle.jce.provider.WrapCipherSpi$RFC3211DESedeWrap");
         // put("Cipher.SERPENT", "org.bouncycastle.jce.provider.JCEBlockCipher$Serpent");
-        // put("Cipher.CAMELLIA", "org.bouncycastle.jce.provider.JCEBlockCipher$Camellia");
-        // put("Cipher.CAST5", "org.bouncycastle.jce.provider.JCEBlockCipher$CAST5");
-        // put("Cipher.1.2.840.113533.7.66.10", "org.bouncycastle.jce.provider.JCEBlockCipher$CAST5CBC");
+        // END android-removed
+
+
+        // BEGIN android-removed
         // put("Cipher.CAST6", "org.bouncycastle.jce.provider.JCEBlockCipher$CAST6");
-        // put("Cipher.IDEA", "org.bouncycastle.jce.provider.JCEBlockCipher$IDEA");
-        // put("Cipher.1.3.6.1.4.1.188.7.1.1.2", "org.bouncycastle.jce.provider.JCEBlockCipher$IDEACBC");
         // END android-removed
         put("Alg.Alias.Cipher.PBEWithSHAAnd3KeyTripleDES",  "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
-
+        
         // BEGIN android-removed
         // put("Cipher.GOST28147", "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147");
         // put("Alg.Alias.Cipher.GOST", "GOST28147");
         // put("Alg.Alias.Cipher.GOST-28147", "GOST28147");
-
         // put("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, "org.bouncycastle.jce.provider.JCEBlockCipher$GOST28147cbc");
-
-        // put("Cipher.DES/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$DES_CFB8");
-        // put("Cipher.DESEDE/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$DESede_CFB8");
-        // put("Cipher.SKIPJACK/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Skipjack_CFB8");
-        // put("Cipher.BLOWFISH/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Blowfish_CFB8");
-        // put("Cipher.TWOFISH/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Twofish_CFB8");
-        // put("Cipher.IDEA/CFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$IDEA_CFB8");
-
-        // put("Alg.Alias.Cipher.DES/CFB8/NOPADDING", "DES/CFB8");
-        // put("Alg.Alias.Cipher.DESEDE/CFB8/NOPADDING", "DESEDE/CFB8");
-        // put("Alg.Alias.Cipher.SKIPJACK/CFB8/NOPADDING", "SKIPJACK/CFB8");
-        // put("Alg.Alias.Cipher.BLOWFISH/CFB8/NOPADDING", "Blowfish/CFB8");
-        // put("Alg.Alias.Cipher.TWOFISH/CFB8/NOPADDING", "Twofish/CFB8");
-        // put("Alg.Alias.Cipher.IDEA/CFB8/NOPADDING", "IDEA/CFB8");
-
-        // put("Cipher.DES/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$DES_OFB8");
-        // put("Cipher.DESEDE/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$DESede_OFB8");
-        // put("Cipher.SKIPJACK/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Skipjack_OFB8");
-        // put("Cipher.BLOWFISH/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Blowfish_OFB8");
-        // put("Cipher.TWOFISH/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$Twofish_OFB8");
-        // put("Cipher.IDEA/OFB8", "org.bouncycastle.jce.provider.JCEStreamCipher$IDEA_OFB8");
-
-        // put("Alg.Alias.Cipher.DES/OFB8/NOPADDING", "DES/OFB8");
-        // put("Alg.Alias.Cipher.DESEDE/OFB8/NOPADDING", "DESEDE/OFB8");
-        // put("Alg.Alias.Cipher.SKIPJACK/OFB8/NOPADDING", "SKIPJACK/OFB8");
-        // put("Alg.Alias.Cipher.BLOWFISH/OFB8/NOPADDING", "BLOWFISH/OFB8");
-        // put("Alg.Alias.Cipher.TWOFISH/OFB8/NOPADDING", "TWOFISH/OFB8");
-        // put("Alg.Alias.Cipher.IDEA/OFB8/NOPADDING", "IDEA/OFB8");
+        //
+        // put("Cipher.TEA", "org.bouncycastle.jce.provider.JCEBlockCipher$TEA");
+        // put("Cipher.XTEA", "org.bouncycastle.jce.provider.JCEBlockCipher$XTEA");
         // END android-removed
-        
+
         put("Cipher.RSA", "org.bouncycastle.jce.provider.JCERSACipher$NoPadding");
-        put("Cipher.RSA/RAW", "org.bouncycastle.jce.provider.JCERSACipher$NoPadding");
-        put("Cipher.RSA/PKCS1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
-        put("Cipher.1.2.840.113549.1.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
-        put("Cipher.2.5.8.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
-        put("Cipher.RSA/1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PrivateOnly");
-        put("Cipher.RSA/2", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PublicOnly");
-        put("Cipher.RSA/OAEP", "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
-        put("Cipher.1.2.840.113549.1.1.7", "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
-        put("Cipher.RSA/ISO9796-1", "org.bouncycastle.jce.provider.JCERSACipher$ISO9796d1Padding");
+        // BEGIN android-changed
+        put("Alg.Alias.Cipher.RSA/RAW", "RSA");
+        // END android-changed
+        // BEGIN android-removed
+        // put("Cipher.RSA/PKCS1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+        // put("Cipher.1.2.840.113549.1.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+        // put("Cipher.2.5.8.1.1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding");
+        // put("Cipher.RSA/1", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PrivateOnly");
+        // put("Cipher.RSA/2", "org.bouncycastle.jce.provider.JCERSACipher$PKCS1v1_5Padding_PublicOnly");
+        // put("Cipher.RSA/OAEP", "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
+        // put("Cipher." + PKCSObjectIdentifiers.id_RSAES_OAEP, "org.bouncycastle.jce.provider.JCERSACipher$OAEPPadding");
+        // put("Cipher.RSA/ISO9796-1", "org.bouncycastle.jce.provider.JCERSACipher$ISO9796d1Padding");
+        // END android-removed
 
         // BEGIN android-removed
         // put("Cipher.ECIES", "org.bouncycastle.jce.provider.JCEIESCipher$ECIES");
         // put("Cipher.BrokenECIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenECIES");
-        // END android-removed
-        put("Cipher.IES", "org.bouncycastle.jce.provider.JCEIESCipher$IES");
-        put("Cipher.BrokenIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenIES");
-        // BEGIN android-removed
+        // put("Cipher.IES", "org.bouncycastle.jce.provider.JCEIESCipher$IES");
+        // put("Cipher.BrokenIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenIES");
         // put("Cipher.ELGAMAL", "org.bouncycastle.jce.provider.JCEElGamalCipher$NoPadding");
         // put("Cipher.ELGAMAL/PKCS1", "org.bouncycastle.jce.provider.JCEElGamalCipher$PKCS1v1_5Padding");
         // END android-removed
 
         put("Alg.Alias.Cipher.RSA//RAW", "RSA");
         put("Alg.Alias.Cipher.RSA//NOPADDING", "RSA");
-        put("Alg.Alias.Cipher.RSA//PKCS1PADDING", "RSA/PKCS1");
-        put("Alg.Alias.Cipher.RSA//OAEPPADDING", "RSA/OAEP");
-        put("Alg.Alias.Cipher.RSA//ISO9796-1PADDING", "RSA/ISO9796-1");
+        // BEGIN android-removed
+        // put("Alg.Alias.Cipher.RSA//PKCS1PADDING", "RSA/PKCS1");
+        // put("Alg.Alias.Cipher.RSA//OAEPPADDING", "RSA/OAEP");
+        // put("Alg.Alias.Cipher.RSA//ISO9796-1PADDING", "RSA/ISO9796-1");
+        // END android-removed
         
         // BEGIN android-removed
         // put("Alg.Alias.Cipher.ELGAMAL/ECB/PKCS1PADDING", "ELGAMAL/PKCS1");
@@ -322,57 +378,58 @@
         // END android-removed
 
         put("Cipher.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndDES");
-        put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES");
-        put("Cipher.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndRC2");
-        put("Cipher.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndDES");
-        put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES");
         // BEGIN android-removed
+        // put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES");
+        // put("Cipher.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndRC2");
+        // put("Cipher.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndDES");
+        // put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES");
         // put("Cipher.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndRC2");
         // END android-removed
         put("Cipher.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES3Key");
-        put("Cipher.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES3Key");
-        put("Cipher.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndDES3Key");
-        put("Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES2Key");
-        put("Cipher.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES2Key");
         // BEGIN android-removed
+        // put("Cipher.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES3Key");
+        // put("Cipher.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndDES3Key");
+        // put("Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndDES2Key");
+        // put("Cipher.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHAAndDES2Key");
         // put("Cipher.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd128BitRC2");
-        // END android-removed
-        put("Cipher.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd40BitRC2");
-        // BEGIN android-removed
+        // put("Cipher.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd40BitRC2");
         // put("Cipher.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd128BitRC4");
         // put("Cipher.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd40BitRC4");
         // END android-removed
-        
-        put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND3-KEYTRIPLEDES-CBC", "Cipher.PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND2-KEYTRIPLEDES-CBC", "Cipher.PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+
+        // BEGIN android-changed
+        put("Alg.Alias.Cipher.PBEWITHSHA1AND3-KEYTRIPLEDES-CBC", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND2-KEYTRIPLEDES-CBC", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC2-CBC", "PBEWITHSHAAND128BITRC2-CBC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC2-CBC", "PBEWITHSHAAND40BITRC2-CBC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC4", "PBEWITHSHAAND128BITRC4");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC4", "PBEWITHSHAAND40BITRC4");
+        // END android-changed
+
         // BEGIN android-removed
-        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC2-CBC", "Cipher.PBEWITHSHAAND128BITRC2-CBC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
+        //
+        // put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+        // put("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
         // END android-removed
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC2-CBC", "Cipher.PBEWITHSHAAND40BITRC2-CBC");
-        // BEGIN android-removed
-        // put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC4", "Cipher.PBEWITHSHAAND128BITRC4");
-        // put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC4", "Cipher.PBEWITHSHAAND40BITRC4");
-        // END android-removed
-        
-        put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-        put("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
         
         put("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
         put("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
@@ -381,29 +438,31 @@
         // BEGIN android-removed
         // put("Cipher.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndTwofish");
         // put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
-        // put("Cipher.PBEWITHSHAANDIDEA-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndIDEA");
         //
         // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
         // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
         // END android-removed
         put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.3", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
         // BEGIN android-removed
+        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
         // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+        // put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
         // END android-removed
-        put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
         put("Alg.Alias.Cipher.PBEWITHSHA1ANDDESEDE", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
         //
         // key generators.
         //
         put("KeyGenerator.DES", "org.bouncycastle.jce.provider.JCEKeyGenerator$DES");
-        put("Alg.Alias.KeyGenerator.1.3.14.3.2.7", "DES");
+        put("Alg.Alias.KeyGenerator." + OIWObjectIdentifiers.desCBC, "DES");
         put("KeyGenerator.DESEDE", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
-        put("KeyGenerator.1.2.840.113549.3.7", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede3");
-        put("KeyGenerator.DESEDEWRAP", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
         // BEGIN android-removed
+        // put("KeyGenerator." + PKCSObjectIdentifiers.des_EDE3_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede3");
+        // END android-removed
+        // BEGIN android-removed
+        // put("KeyGenerator.DESEDEWRAP", "org.bouncycastle.jce.provider.JCEKeyGenerator$DESede");
         // put("KeyGenerator.SKIPJACK", "org.bouncycastle.jce.provider.JCEKeyGenerator$Skipjack");
         // put("KeyGenerator.BLOWFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Blowfish");
+        // put("Alg.Alias.KeyGenerator.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
         // put("KeyGenerator.TWOFISH", "org.bouncycastle.jce.provider.JCEKeyGenerator$Twofish");
         // put("KeyGenerator.RC2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
         // put("KeyGenerator.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC2");
@@ -415,36 +474,20 @@
         // put("KeyGenerator.RC5-64", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC564");
         // put("KeyGenerator.RC6", "org.bouncycastle.jce.provider.JCEKeyGenerator$RC6");
         // put("KeyGenerator.RIJNDAEL", "org.bouncycastle.jce.provider.JCEKeyGenerator$Rijndael");
-        // END android-removed
-        put("KeyGenerator.AES", "org.bouncycastle.jce.provider.JCEKeyGenerator$AES");
-        put("KeyGenerator.2.16.840.1.101.3.4.2", "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator.2.16.840.1.101.3.4.22", "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator.2.16.840.1.101.3.4.42", "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        put("KeyGenerator.AESWRAP", "org.bouncycastle.jce.provider.JCEKeyGenerator$AES");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_wrap, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES128");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_wrap, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES192");
-        put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_wrap, "org.bouncycastle.jce.provider.JCEKeyGenerator$AES256");
-        // BEGIN android-removed
+        //
         // put("KeyGenerator.SERPENT", "org.bouncycastle.jce.provider.JCEKeyGenerator$Serpent");
-        // put("KeyGenerator.CAMELLIA", "org.bouncycastle.jce.provider.JCEKeyGenerator$Camellia");
-        // put("KeyGenerator.CAST5", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST5");
-        // put("KeyGenerator.1.2.840.113533.7.66.10", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST5");
-        // put("KeyGenerator.CAST6", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST6");
-        // put("KeyGenerator.IDEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$IDEA");
-        // put("KeyGenerator.1.3.6.1.4.1.188.7.1.1.2", "org.bouncycastle.jce.provider.JCEKeyGenerator$IDEA");
+        // put("KeyGenerator.SALSA20", "org.bouncycastle.jce.provider.JCEKeyGenerator$Salsa20");
+        // put("KeyGenerator.HC128", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC128");
+        // put("KeyGenerator.HC256", "org.bouncycastle.jce.provider.JCEKeyGenerator$HC256");
+        // put("KeyGenerator.VMPC", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPC");
+        // put("KeyGenerator.VMPC-KSA3", "org.bouncycastle.jce.provider.JCEKeyGenerator$VMPCKSA3");
+        // END android-removed
 
+        // BEGIN android-removed
+        // put("KeyGenerator.CAST6", "org.bouncycastle.jce.provider.JCEKeyGenerator$CAST6");
+        // put("KeyGenerator.TEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$TEA");
+        // put("KeyGenerator.XTEA", "org.bouncycastle.jce.provider.JCEKeyGenerator$XTEA");
+        //
         // put("KeyGenerator.GOST28147", "org.bouncycastle.jce.provider.JCEKeyGenerator$GOST28147");
         // put("Alg.Alias.KeyGenerator.GOST", "GOST28147");
         // put("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147");
@@ -459,25 +502,17 @@
         put("KeyPairGenerator.DSA", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$DSA");
         // BEGIN android-removed
         // put("KeyPairGenerator.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ElGamal");
-        // put("KeyPairGenerator.EC", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$EC");
-        // put("KeyPairGenerator.ECDSA", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ECDSA");
-        // put("KeyPairGenerator.ECDH", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ECDH");
-        // put("KeyPairGenerator.ECDHC", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ECDHC");
-        // put("KeyPairGenerator.ECIES", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ECDH");
         // END android-removed
+
         put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.1", "RSA");
+        put("Alg.Alias.KeyPairGenerator.DIFFIEHELLMAN", "DH");
         
         // BEGIN android-removed
         // put("KeyPairGenerator.GOST3410", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$GOST3410");
         // put("Alg.Alias.KeyPairGenerator.GOST-3410", "GOST3410");
         // put("Alg.Alias.KeyPairGenerator.GOST-3410-94", "GOST3410");
-        
-        // put("KeyPairGenerator.ECGOST3410", "org.bouncycastle.jce.provider.JDKKeyPairGenerator$ECGOST3410");
-        // put("Alg.Alias.KeyPairGenerator.ECGOST-3410", "ECGOST3410");
-        // put("Alg.Alias.KeyPairGenerator.GOST-3410-2001", "ECGOST3410");
         // END android-removed
 
-
         //
         // key factories
         //
@@ -487,80 +522,69 @@
         // BEGIN android-removed
         // put("KeyFactory.ELGAMAL", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
         // put("KeyFactory.ElGamal", "org.bouncycastle.jce.provider.JDKKeyFactory$ElGamal");
-        // put("KeyFactory.EC", "org.bouncycastle.jce.provider.JDKKeyFactory$EC");
-        // put("KeyFactory.ECDSA", "org.bouncycastle.jce.provider.JDKKeyFactory$ECDSA");
-        // put("KeyFactory.ECDH", "org.bouncycastle.jce.provider.JDKKeyFactory$ECDH");
-        // put("KeyFactory.ECDHC", "org.bouncycastle.jce.provider.JDKKeyFactory$ECDHC");
+        //
+        // put("KeyFactory.X.509", "org.bouncycastle.jce.provider.JDKKeyFactory$X509");
         // END android-removed
-        put("KeyFactory.X.509", "org.bouncycastle.jce.provider.JDKKeyFactory$X509");
         
         put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA");
         put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA");
-        // BEGIN android-removed
-        // put("Alg.Alias.KeyFactory." + X9ObjectIdentifiers.id_ecPublicKey, "EC");
-        
 
+        put("Alg.Alias.KeyFactory.DIFFIEHELLMAN", "DH");
+
+        // BEGIN android-removed
         // put("KeyFactory.GOST3410", "org.bouncycastle.jce.provider.JDKKeyFactory$GOST3410");
         // put("Alg.Alias.KeyFactory.GOST-3410", "GOST3410");
         // put("Alg.Alias.KeyFactory.GOST-3410-94", "GOST3410");
         // put("Alg.Alias.KeyFactory." + CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
-        // put("KeyFactory.ECGOST3410", "org.bouncycastle.jce.provider.JDKKeyFactory$ECGOST3410");
-        // put("Alg.Alias.KeyFactory.GOST-3410-2001", "ECGOST3410");
-        // put("Alg.Alias.KeyFactory.ECGOST-3410", "ECGOST3410");
-        // put("Alg.Alias.KeyFactory." + CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
         // END android-removed
 
         //
         // Algorithm parameters
         //
         put("AlgorithmParameters.DES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
-        put("Alg.Alias.AlgorithmParameters.1.3.14.3.2.7", "DES");
+        put("Alg.Alias.AlgorithmParameters." + OIWObjectIdentifiers.desCBC, "DES");
         put("AlgorithmParameters.DESEDE", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
-        put("AlgorithmParameters.1.2.840.113549.3.7", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+        // BEGIN android-changed
+        put("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.des_EDE3_CBC, "DESEDE");
+        // END android-changed
         // BEGIN android-removed
         // put("AlgorithmParameters.RC2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
         // put("AlgorithmParameters.1.2.840.113549.3.2", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$RC2AlgorithmParameters");
         // put("AlgorithmParameters.RC5", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
         // put("AlgorithmParameters.RC6", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
-        // put("AlgorithmParameters.IDEA", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IDEAAlgorithmParameters");
         // put("AlgorithmParameters.BLOWFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
+        // put("Alg.Alias.AlgorithmParameters.1.3.6.1.4.1.3029.1.2", "BLOWFISH");
         // put("AlgorithmParameters.TWOFISH", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
         // put("AlgorithmParameters.SKIPJACK", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
         // put("AlgorithmParameters.RIJNDAEL", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
         // END android-removed
-        put("AlgorithmParameters.AES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IVAlgorithmParameters");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.2", "AES");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.22", "AES");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.42", "AES");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.2", "AES");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.22", "AES");
-        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.42", "AES");
+
         
         //
         // secret key factories.
         //
         put("SecretKeyFactory.DES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$DES");
         put("SecretKeyFactory.DESEDE", "org.bouncycastle.jce.provider.JCESecretKeyFactory$DESede");
-        put("SecretKeyFactory.DESEDE", "org.bouncycastle.jce.provider.JCESecretKeyFactory$DESede");
-        put("SecretKeyFactory.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndDES");
-        put("SecretKeyFactory.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndRC2");
-        put("SecretKeyFactory.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndDES");
-        put("SecretKeyFactory.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndRC2");
-        put("SecretKeyFactory.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES3Key");
-        put("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES2Key");
         // BEGIN android-removed
+        // put("SecretKeyFactory.PBEWITHMD2ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndDES");
+        // put("SecretKeyFactory.PBEWITHMD2ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndRC2");
+        // END android-removed
+        put("SecretKeyFactory.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndDES");
+        // BEGIN android-removed
+        // put("SecretKeyFactory.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndRC2");
+        // put("SecretKeyFactory.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndDES");
+        // put("SecretKeyFactory.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndRC2");
+        // END android-removed
+        put("SecretKeyFactory.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES3Key");
+        // BEGIN android-removed
+        // put("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES2Key");
         // put("SecretKeyFactory.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC4");
         // put("SecretKeyFactory.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC4");
         // put("SecretKeyFactory.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC2");
-        // BEGIN android-removed
-        put("SecretKeyFactory.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC2");
-        // END android-removed
+        // put("SecretKeyFactory.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC2");
         // put("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndTwofish");
-        // put("SecretKeyFactory.PBEWITHSHAANDIDEA-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndIDEA");
         // put("SecretKeyFactory.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithRIPEMD160");
-        // END android-removed
-        put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");
-        // BEGIN android-removed
+        // put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");
         // put("SecretKeyFactory.PBEWITHHMACTIGER", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithTiger");
         // END android-removed
         
@@ -568,16 +592,35 @@
         put("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And192BitAESCBCOpenSSL");
         put("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And256BitAESCBCOpenSSL");
 
-        put("Alg.Alias.SecretKeyFactory.PBE", "PBE/PKCS5");
-
-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHMD5ANDDES", "PBE/PKCS5");
-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHA1ANDDES", "PBE/PKCS5");
-        put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
-        put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBE/PKCS12");
         // BEGIN android-removed
+        // put("Alg.Alias.SecretKeyFactory.PBE", "PBE/PKCS5");
+        //
+        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHMD5ANDDES", "PBE/PKCS5");
+        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHA1ANDDES", "PBE/PKCS5");
+        // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+        // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBE/PKCS12");
         // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAANDTWOFISH-CBC", "PBE/PKCS12");
+        //
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDDES-CBC", "PBEWITHMD2ANDDES");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDRC2-CBC", "PBEWITHMD2ANDRC2");
+        // END android-removed
+        put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDDES-CBC", "PBEWITHMD5ANDDES");
+        // BEGIN android-removed
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDRC2-CBC", "PBEWITHMD5ANDRC2");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDDES-CBC", "PBEWITHSHA1ANDDES");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDRC2-CBC", "PBEWITHSHA1ANDRC2");
+        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
+        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
+        // END android-removed
+        put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBEWITHMD5ANDDES");
+        // BEGIN android-removed
+        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDRC2");
+        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
+        // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
+        // END android-removed
 
+        // BEGIN android-removed
         // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
         // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
         // END android-removed
@@ -585,28 +628,35 @@
         // BEGIN android-removed
         // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
         // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+        // put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA", "PBEWITHHMACSHA1");
+        // put("Alg.Alias.SecretKeyFactory.1.3.14.3.2.26", "PBEWITHHMACSHA1");
         // END android-removed
-        put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
-
-        put("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA", "PBEWITHHMACSHA1");
-        put("Alg.Alias.SecretKeyFactory.1.3.14.3.2.26", "PBEWITHHMACSHA1");
         put("Alg.Alias.SecretKeyFactory.PBEWithSHAAnd3KeyTripleDES", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
         
-        put("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitAESBC");
-        put("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd192BitAESBC");
-        put("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd256BitAESBC");
-        put("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And128BitAESBC");
-        put("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And192BitAESBC");
-        put("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And256BitAESBC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-        put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+        // BEGIN android-removed
+        // put("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitAESBC");
+        // put("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd192BitAESBC");
+        // put("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd256BitAESBC");
+        // put("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And128BitAESBC");
+        // put("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And192BitAESBC");
+        // put("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And256BitAESBC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+        // put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
+        // END android-removed
 
         addMacAlgorithms();
 
@@ -615,48 +665,115 @@
         addSignatureAlgorithms();
 
     // Certification Path API
+        // BEGIN android-removed
+        // put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
+        // put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
+        // END android-removed
+        // BEGIN android-changed
+        // Use Alg.Alias so RFC3280 doesn't show up when iterating provider services, only PKIX
+        put("Alg.Alias.CertPathValidator.RFC3280", "PKIX");
+        put("Alg.Alias.CertPathBuilder.RFC3280", "PKIX");
+        // END android-changed
         put("CertPathValidator.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
-        put("CertPathValidator.PKIX ValidationAlgorithm", "RFC2459");
         put("CertPathBuilder.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
-        put("CertPathBuilder.PKIX ValidationAlgorithm", "RFC2459");
         put("CertStore.Collection", "org.bouncycastle.jce.provider.CertStoreCollectionSpi");
+        // BEGIN android-removed
+        // put("CertStore.LDAP", "org.bouncycastle.jce.provider.X509LDAPCertStoreSpi");
+        // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
+        // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
+        // END android-removed
     }
-    
+
+    private void loadAlgorithms(String packageName, String[] names)
+    {
+        for (int i = 0; i != names.length; i++)
+        {
+            Class clazz = null;
+            try
+            {
+                ClassLoader loader = this.getClass().getClassLoader();
+
+                if (loader != null)
+                {
+                    clazz = loader.loadClass(packageName + names[i] + "Mappings");
+                }
+                else
+                {
+                    clazz = Class.forName(packageName + names[i] + "Mappings");
+                }
+            }
+            catch (ClassNotFoundException e)
+            {
+                // ignore
+            }
+
+            if (clazz != null)
+            {
+                try
+                {
+                    addMappings((Map)clazz.newInstance());
+                }
+                catch (Exception e)
+                {   // this should never ever happen!!
+                    throw new InternalError("cannot create instance of "
+                        + packageName + names[i] + "Mappings : " + e);
+                }
+            }
+        }
+    }
+
+    private void addMappings(Map mappings)
+    {
+        // can't use putAll due to JDK 1.1
+        for (Iterator it = mappings.keySet().iterator(); it.hasNext();)
+        {
+            Object key = it.next();
+
+            if (containsKey(key))
+            {
+                throw new IllegalStateException("duplicate provider key (" + key + ") found in " + mappings.getClass().getName());
+            }
+            put(key, mappings.get(key));
+        }
+    }
+
     //
     // macs
     //
     private void addMacAlgorithms()
     {
-        put("Mac.DESMAC", "org.bouncycastle.jce.provider.JCEMac$DES");
-        put("Alg.Alias.Mac.DES", "DESMAC");
-        put("Mac.DESMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESCFB8");
-        put("Alg.Alias.Mac.DES/CFB8", "DESMAC/CFB8");
-
-        put("Mac.DESEDEMAC", "org.bouncycastle.jce.provider.JCEMac$DESede");
-        put("Alg.Alias.Mac.DESEDE", "DESEDEMAC");
-        put("Mac.DESEDEMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESedeCFB8");
-        put("Alg.Alias.Mac.DESEDE/CFB8", "DESEDEMAC/CFB8");
-        
-        put("Mac.DESWITHISO9797", "org.bouncycastle.jce.provider.JCEMac$ISO9797_DES");
-        put("Alg.Alias.Mac.DESISO9797MAC", "DESWITHISO9797");
-        
-        put("Mac.DESEDEMAC64", "org.bouncycastle.jce.provider.JCEMac$DESede64");
-        put("Alg.Alias.Mac.DESEDE64", "DESEDEMAC64");
-        
-        put("Mac.ISO9797ALG3MAC", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
-        put("Alg.Alias.Mac.ISO9797ALG3", "ISO9797ALG3MAC");
-
         // BEGIN android-removed
+        // put("Mac.DESMAC", "org.bouncycastle.jce.provider.JCEMac$DES");
+        // put("Alg.Alias.Mac.DES", "DESMAC");
+        // put("Mac.DESMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESCFB8");
+        // put("Alg.Alias.Mac.DES/CFB8", "DESMAC/CFB8");
+        //
+        // put("Mac.DESEDEMAC", "org.bouncycastle.jce.provider.JCEMac$DESede");
+        // put("Alg.Alias.Mac.DESEDE", "DESEDEMAC");
+        // put("Mac.DESEDEMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$DESedeCFB8");
+        // put("Alg.Alias.Mac.DESEDE/CFB8", "DESEDEMAC/CFB8");
+        //
+        // put("Mac.DESWITHISO9797", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+        // put("Alg.Alias.Mac.DESISO9797MAC", "DESWITHISO9797");
+        //
+        // put("Mac.DESEDEMAC64", "org.bouncycastle.jce.provider.JCEMac$DESede64");
+        // put("Alg.Alias.Mac.DESEDE64", "DESEDEMAC64");
+        //
+        // put("Mac.DESEDEMAC64WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DESede64with7816d4");
+        // put("Alg.Alias.Mac.DESEDE64WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+        // put("Alg.Alias.Mac.DESEDEISO9797ALG1MACWITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+        // put("Alg.Alias.Mac.DESEDEISO9797ALG1WITHISO7816-4PADDING", "DESEDEMAC64WITHISO7816-4PADDING");
+        //
+        // put("Mac.ISO9797ALG3MAC", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+        // put("Alg.Alias.Mac.ISO9797ALG3", "ISO9797ALG3MAC");
+        // put("Mac.ISO9797ALG3WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3with7816d4");
+        // put("Alg.Alias.Mac.ISO9797ALG3MACWITHISO7816-4PADDING", "ISO9797ALG3WITHISO7816-4PADDING");
+        //
         // put("Mac.SKIPJACKMAC", "org.bouncycastle.jce.provider.JCEMac$Skipjack");
         // put("Alg.Alias.Mac.SKIPJACK", "SKIPJACKMAC");
         // put("Mac.SKIPJACKMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$SkipjackCFB8");
         // put("Alg.Alias.Mac.SKIPJACK/CFB8", "SKIPJACKMAC/CFB8");
         //
-        // put("Mac.IDEAMAC", "org.bouncycastle.jce.provider.JCEMac$IDEA");
-        // put("Alg.Alias.Mac.IDEA", "IDEAMAC");
-        // put("Mac.IDEAMAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$IDEACFB8");
-        // put("Alg.Alias.Mac.IDEA/CFB8", "IDEAMAC/CFB8");
-        //
         // put("Mac.RC2MAC", "org.bouncycastle.jce.provider.JCEMac$RC2");
         // put("Alg.Alias.Mac.RC2", "RC2MAC");
         // put("Mac.RC2MAC/CFB8", "org.bouncycastle.jce.provider.JCEMac$RC2CFB8");
@@ -668,6 +785,11 @@
         // put("Alg.Alias.Mac.RC5/CFB8", "RC5MAC/CFB8");
         //
         // put("Mac.GOST28147MAC", "org.bouncycastle.jce.provider.JCEMac$GOST28147");
+        // put("Alg.Alias.Mac.GOST28147", "GOST28147MAC");
+        //
+        // put("Mac.VMPCMAC", "org.bouncycastle.jce.provider.JCEMac$VMPC");
+        // put("Alg.Alias.Mac.VMPC", "VMPCMAC");
+        // put("Alg.Alias.Mac.VMPC-MAC", "VMPCMAC");
         //
         // put("Mac.OLDHMACSHA384", "org.bouncycastle.jce.provider.JCEMac$OldSHA384");
         //
@@ -682,8 +804,10 @@
         addHMACAlgorithm("SHA1", "org.bouncycastle.jce.provider.JCEMac$SHA1", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA1");
         addHMACAlias("SHA1", PKCSObjectIdentifiers.id_hmacWithSHA1);
         addHMACAlias("SHA1", IANAObjectIdentifiers.hmacSHA1);
-        addHMACAlgorithm("SHA224", "org.bouncycastle.jce.provider.JCEMac$SHA224", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA224");
-        addHMACAlias("SHA224", PKCSObjectIdentifiers.id_hmacWithSHA224);
+        // BEGIN android-removed
+        // addHMACAlgorithm("SHA224", "org.bouncycastle.jce.provider.JCEMac$SHA224", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA224");
+        // addHMACAlias("SHA224", PKCSObjectIdentifiers.id_hmacWithSHA224);
+        // END android-removed
         addHMACAlgorithm("SHA256", "org.bouncycastle.jce.provider.JCEMac$SHA256", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA256");
         addHMACAlias("SHA256", PKCSObjectIdentifiers.id_hmacWithSHA256);
         addHMACAlgorithm("SHA384", "org.bouncycastle.jce.provider.JCEMac$SHA384", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACSHA384");
@@ -692,20 +816,18 @@
         addHMACAlias("SHA512", PKCSObjectIdentifiers.id_hmacWithSHA512);
 
         // BEGIN android-removed
-        //addHMACAlgorithm("RIPEMD128", "org.bouncycastle.jce.provider.JCEMac$RIPEMD128", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD128HMAC");
-        //addHMACAlgorithm("RIPEMD160", "org.bouncycastle.jce.provider.JCEMac$RIPEMD160", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD160HMAC");
-        //addHMACAlias("RIPEMD160", IANAObjectIdentifiers.hmacRIPEMD160);
-
+        // addHMACAlgorithm("RIPEMD128", "org.bouncycastle.jce.provider.JCEMac$RIPEMD128", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD128HMAC");
+        // addHMACAlgorithm("RIPEMD160", "org.bouncycastle.jce.provider.JCEMac$RIPEMD160", "org.bouncycastle.jce.provider.JCEKeyGenerator$RIPEMD160HMAC");
+        // addHMACAlias("RIPEMD160", IANAObjectIdentifiers.hmacRIPEMD160);
+        //
         // addHMACAlgorithm("TIGER", "org.bouncycastle.jce.provider.JCEMac$Tiger", "org.bouncycastle.jce.provider.JCEKeyGenerator$HMACTIGER");
         // addHMACAlias("TIGER", IANAObjectIdentifiers.hmacTIGER);
-        // END android-removed
-
-        put("Mac.PBEWITHHMACSHA", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
-        put("Mac.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
-        // BEGIN android-removed
+        //
+        // put("Mac.PBEWITHHMACSHA", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
+        // put("Mac.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
         // put("Mac.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCEMac$PBEWithRIPEMD160");
+        // put("Alg.Alias.Mac.1.3.14.3.2.26", "PBEWITHHMACSHA");
         // END android-removed
-        put("Alg.Alias.Mac.1.3.14.3.2.26", "PBEWITHHMACSHA");
     }
 
     private void addHMACAlgorithm(
@@ -738,18 +860,18 @@
     //
     private void addMessageDigestAlgorithms()
     {
+        put("MessageDigest.SHA-1", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA1");
+        put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
+        put("Alg.Alias.MessageDigest.SHA", "SHA-1");
+        put("Alg.Alias.MessageDigest." + OIWObjectIdentifiers.idSHA1, "SHA-1");
         // BEGIN android-removed
-        // put("MessageDigest.SHA-1", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA1");
-        // put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
-        // put("Alg.Alias.MessageDigest.SHA", "SHA-1");
-        // put("Alg.Alias.MessageDigest." + OIWObjectIdentifiers.idSHA1, "SHA-1");
         // put("MessageDigest.SHA-224", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA224");
         // put("Alg.Alias.MessageDigest.SHA224", "SHA-224");
         // put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha224, "SHA-224");
-        // put("MessageDigest.SHA-256", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA256");
-        // put("Alg.Alias.MessageDigest.SHA256", "SHA-256");
-        // put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha256, "SHA-256");
         // END android-removed
+        put("MessageDigest.SHA-256", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA256");
+        put("Alg.Alias.MessageDigest.SHA256", "SHA-256");
+        put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha256, "SHA-256");
         put("MessageDigest.SHA-384", "org.bouncycastle.jce.provider.JDKMessageDigest$SHA384");
         put("Alg.Alias.MessageDigest.SHA384", "SHA-384");
         put("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha384, "SHA-384");
@@ -762,8 +884,10 @@
         // put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md2, "MD2");
         // put("MessageDigest.MD4", "org.bouncycastle.jce.provider.JDKMessageDigest$MD4");
         // put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md4, "MD4");
-        // put("MessageDigest.MD5", "org.bouncycastle.jce.provider.JDKMessageDigest$MD5");
-        // put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md5, "MD5");
+        // END android-removed
+        put("MessageDigest.MD5", "org.bouncycastle.jce.provider.JDKMessageDigest$MD5");
+        put("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md5, "MD5");
+        // BEGIN android-removed
         // put("MessageDigest.RIPEMD128", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD128");
         // put("Alg.Alias.MessageDigest." + TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         // put("MessageDigest.RIPEMD160", "org.bouncycastle.jce.provider.JDKMessageDigest$RIPEMD160");
@@ -790,11 +914,13 @@
         // BEGIN android-removed
         // Dropping MD2
         // put("Signature.MD2WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD2WithRSAEncryption");
+        // put("Signature.MD4WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD4WithRSAEncryption");
         // END android-removed
-        put("Signature.MD4WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD4WithRSAEncryption");
         put("Signature.MD5WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$MD5WithRSAEncryption");
         put("Signature.SHA1WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA1WithRSAEncryption");
-        put("Signature.SHA224WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA224WithRSAEncryption");
+        // BEGIN android-removed
+        // put("Signature.SHA224WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA224WithRSAEncryption");
+        // END android-removed
         put("Signature.SHA256WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA256WithRSAEncryption");
         put("Signature.SHA384WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA384WithRSAEncryption");
         put("Signature.SHA512WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$SHA512WithRSAEncryption");
@@ -803,46 +929,52 @@
         // put("Signature.RIPEMD128WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD128WithRSAEncryption");
         // put("Signature.RIPEMD256WithRSAEncryption", "org.bouncycastle.jce.provider.JDKDigestSignature$RIPEMD256WithRSAEncryption");
         // END android-removed
-        put("Signature.DSA", "org.bouncycastle.jce.provider.JDKDSASigner$stdDSA");
+        // BEGIN android-changed
+        put("Signature.SHA1withDSA", "org.bouncycastle.jce.provider.JDKDSASigner$stdDSA");
+        // END android-changed
         put("Signature.NONEWITHDSA", "org.bouncycastle.jce.provider.JDKDSASigner$noneDSA");
         // BEGIN android-removed
-        // put("Signature.ECDSA", "org.bouncycastle.jce.provider.JDKDSASigner$ecDSA");
-        // put("Signature.SHA1WITHECNR", "org.bouncycastle.jce.provider.JDKDSASigner$ecNR");
-        // put("Signature.SHA224WITHECNR", "org.bouncycastle.jce.provider.JDKDSASigner$ecNR224");
-        // put("Signature.SHA256WITHECNR", "org.bouncycastle.jce.provider.JDKDSASigner$ecNR256");
-        // put("Signature.SHA384WITHECNR", "org.bouncycastle.jce.provider.JDKDSASigner$ecNR384");
-        // put("Signature.SHA512WITHECNR", "org.bouncycastle.jce.provider.JDKDSASigner$ecNR512");
-        // END android-removed
-        put("Signature.SHA1withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$SHA1WithRSAEncryption");
-        put("Signature.MD5withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$MD5WithRSAEncryption");
-        // BEGIN android-removed
+        // put("Signature.SHA1withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$SHA1WithRSAEncryption");
+        // put("Signature.MD5withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$MD5WithRSAEncryption");
         // put("Signature.RIPEMD160withRSA/ISO9796-2", "org.bouncycastle.jce.provider.JDKISOSignature$RIPEMD160WithRSAEncryption");
+        //
+        // put("Signature.RSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
+        // put("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
+        // put("Signature.SHA1withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA1withRSA");
+        // put("Signature.SHA224withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA224withRSA");
+        // put("Signature.SHA256withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA256withRSA");
+        // put("Signature.SHA384withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA384withRSA");
+        // put("Signature.SHA512withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA512withRSA");
+        //
+        // put("Signature.RSA", "org.bouncycastle.jce.provider.JDKDigestSignature$noneRSA");
+        // put("Signature.RAWRSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$nonePSS");
         // END android-removed
 
-        put("Signature.RSASSA-PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
-        put("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "org.bouncycastle.jce.provider.JDKPSSSigner$PSSwithRSA");
-        put("Signature.SHA1withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA1withRSA");
-        put("Signature.SHA224withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA224withRSA");
-        put("Signature.SHA256withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA256withRSA");
-        put("Signature.SHA384withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA384withRSA");
-        put("Signature.SHA512withRSA/PSS", "org.bouncycastle.jce.provider.JDKPSSSigner$SHA512withRSA");
-
         put("Alg.Alias.Signature.RAWDSA", "NONEWITHDSA");
-        
-        put("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS");
-        put("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS");
-        put("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS");
-        put("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS");
-        put("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS");
-        
+
         // BEGIN android-removed
-        // Dropping MD2
+        // put("Alg.Alias.Signature.RAWRSA", "RSA");
+        // put("Alg.Alias.Signature.NONEWITHRSA", "RSA");
+        // put("Alg.Alias.Signature.RAWRSAPSS", "RAWRSASSA-PSS");
+        // put("Alg.Alias.Signature.NONEWITHRSAPSS", "RAWRSASSA-PSS");
+        // put("Alg.Alias.Signature.NONEWITHRSASSA-PSS", "RAWRSASSA-PSS");
+        //
+        // put("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
+        //
+        // put("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS");
+        // put("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS");
+        // put("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS");
+        // put("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS");
+        // put("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS");
+        //
         // put("Alg.Alias.Signature.MD2withRSAEncryption", "MD2WithRSAEncryption");
+        // put("Alg.Alias.Signature.MD4withRSAEncryption", "MD4WithRSAEncryption");
         // END android-removed
-        put("Alg.Alias.Signature.MD4withRSAEncryption", "MD4WithRSAEncryption");
         put("Alg.Alias.Signature.MD5withRSAEncryption", "MD5WithRSAEncryption");
         put("Alg.Alias.Signature.SHA1withRSAEncryption", "SHA1WithRSAEncryption");
-        put("Alg.Alias.Signature.SHA224withRSAEncryption", "SHA224WithRSAEncryption");
+        // BEGIN android-removed
+        // put("Alg.Alias.Signature.SHA224withRSAEncryption", "SHA224WithRSAEncryption");
+        // END android-removed
 
         put("Alg.Alias.Signature.SHA256withRSAEncryption", "SHA256WithRSAEncryption");
         put("Alg.Alias.Signature.SHA384withRSAEncryption", "SHA384WithRSAEncryption");
@@ -868,14 +1000,18 @@
         put("Alg.Alias.Signature.MD5withRSA", "MD5WithRSAEncryption");
         put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption");
         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5WithRSAEncryption");
-        put("Alg.Alias.Signature.MD4WithRSA", "MD4WithRSAEncryption");
-        put("Alg.Alias.Signature.MD4withRSA", "MD4WithRSAEncryption");
-        put("Alg.Alias.Signature.MD4/RSA", "MD4WithRSAEncryption");
-        put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4WithRSAEncryption");
+        // BEGIN android-removed
+        // put("Alg.Alias.Signature.MD4WithRSA", "MD4WithRSAEncryption");
+        // put("Alg.Alias.Signature.MD4withRSA", "MD4WithRSAEncryption");
+        // put("Alg.Alias.Signature.MD4/RSA", "MD4WithRSAEncryption");
+        // put("Alg.Alias.Signature." + PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4WithRSAEncryption");
+        // END android-removed
         put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption");
         put("Alg.Alias.Signature.SHA1withRSA", "SHA1WithRSAEncryption");
-        put("Alg.Alias.Signature.SHA224WithRSA", "SHA224WithRSAEncryption");
-        put("Alg.Alias.Signature.SHA224withRSA", "SHA224WithRSAEncryption");
+        // BEGIN android-removed
+        // put("Alg.Alias.Signature.SHA224WithRSA", "SHA224WithRSAEncryption");
+        // put("Alg.Alias.Signature.SHA224withRSA", "SHA224WithRSAEncryption");
+        // END android-removed
         put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption");
         put("Alg.Alias.Signature.SHA256withRSA", "SHA256WithRSAEncryption");
         put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption");
@@ -885,7 +1021,9 @@
         put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption");
         put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption");
         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1WithRSAEncryption");
-        put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WithRSAEncryption");
+        // BEGIN android-removed
+        // put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WithRSAEncryption");
+        // END android-removed
         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WithRSAEncryption");
         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WithRSAEncryption");
         put("Alg.Alias.Signature." + PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WithRSAEncryption");
@@ -909,7 +1047,6 @@
         put("Alg.Alias.Signature." + OIWObjectIdentifiers.sha1WithRSA, "SHA1WithRSAEncryption");
         
         // BEGIN android-removed
-        // Dropping MD2
         // put("Alg.Alias.Signature.MD2WITHRSAENCRYPTION", "MD2WithRSAEncryption");
         // END android-removed
         put("Alg.Alias.Signature.MD5WITHRSAENCRYPTION", "MD5WithRSAEncryption");
@@ -922,40 +1059,34 @@
         put("Alg.Alias.Signature.SHA1WITHRSA", "SHA1WithRSAEncryption");
         // BEGIN android-removed
         // put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
-        // END android-removed
-        put("Alg.Alias.Signature.RMD160WITHRSA", "RIPEMD160WithRSAEncryption");
-        // BEGIN android-removed
+        // put("Alg.Alias.Signature.RMD160WITHRSA", "RIPEMD160WithRSAEncryption");
         // put("Alg.Alias.Signature.RIPEMD160WITHRSA", "RIPEMD160WithRSAEncryption");
-
-        // put("Alg.Alias.Signature.SHA1withECDSA", "ECDSA");
-        // put("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA");
-        // put("Alg.Alias.Signature.SHA1WITHECDSA", "ECDSA");
-        // put("Alg.Alias.Signature.ECDSAWITHSHA1", "ECDSA");
-        // put("Alg.Alias.Signature.SHA1WithECDSA", "ECDSA");
-        // put("Alg.Alias.Signature.ECDSAWithSHA1", "ECDSA");
-        // put("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
-
-        // addSignatureAlgorithm("SHA224", "ECDSA", "org.bouncycastle.jce.provider.JDKDSASigner$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224);
-        // addSignatureAlgorithm("SHA256", "ECDSA", "org.bouncycastle.jce.provider.JDKDSASigner$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256);
-        // addSignatureAlgorithm("SHA384", "ECDSA", "org.bouncycastle.jce.provider.JDKDSASigner$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384);
-        // addSignatureAlgorithm("SHA512", "ECDSA", "org.bouncycastle.jce.provider.JDKDSASigner$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512);
         // END android-removed
 
-        put("Alg.Alias.Signature.SHA/DSA", "DSA");
-        put("Alg.Alias.Signature.SHA1withDSA", "DSA");
-        put("Alg.Alias.Signature.SHA1WITHDSA", "DSA");
-        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "DSA");
-        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "DSA");
-        put("Alg.Alias.Signature.DSAwithSHA1", "DSA");
-        put("Alg.Alias.Signature.DSAWITHSHA1", "DSA");
-        put("Alg.Alias.Signature.SHA1WithDSA", "DSA");
-        put("Alg.Alias.Signature.DSAWithSHA1", "DSA");
-        put("Alg.Alias.Signature.1.2.840.10040.4.3", "DSA");
-        put("Alg.Alias.Signature.MD5WithRSA/ISO9796-2", "MD5withRSA/ISO9796-2");
-        put("Alg.Alias.Signature.SHA1WithRSA/ISO9796-2", "SHA1withRSA/ISO9796-2");
         // BEGIN android-removed
+        // addSignatureAlgorithm("SHA224", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224);
+        // addSignatureAlgorithm("SHA256", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256);
+        // addSignatureAlgorithm("SHA384", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384);
+        // addSignatureAlgorithm("SHA512", "DSA", "org.bouncycastle.jce.provider.JDKDSASigner$dsa512", NISTObjectIdentifiers.dsa_with_sha512);
+        // END android-removed
+
+        // BEGIN android-changed
+        put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.SHA1WITHDSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.1", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSAwithSHA1", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSAWITHSHA1", "SHA1withDSA");
+        put("Alg.Alias.Signature.SHA1WithDSA", "SHA1withDSA");
+        put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
+        put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA");
+        // END android-changed
+        // BEGIN android-removed
+        // put("Alg.Alias.Signature.MD5WithRSA/ISO9796-2", "MD5withRSA/ISO9796-2");
+        // put("Alg.Alias.Signature.SHA1WithRSA/ISO9796-2", "SHA1withRSA/ISO9796-2");
         // put("Alg.Alias.Signature.RIPEMD160WithRSA/ISO9796-2", "RIPEMD160withRSA/ISO9796-2");
-        
+        //
         // put("Signature.ECGOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$ecgost3410");
         // put("Alg.Alias.Signature.ECGOST-3410", "ECGOST3410");
         // put("Alg.Alias.Signature.GOST-3410-2001", "ECGOST3410");
@@ -963,7 +1094,7 @@
         // put("Alg.Alias.Signature.GOST3411WITHECGOST3410", "ECGOST3410");
         // put("Alg.Alias.Signature.GOST3411WithECGOST3410", "ECGOST3410");
         // put("Alg.Alias.Signature." + CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
-        
+        //
         // put("Signature.GOST3410", "org.bouncycastle.jce.provider.JDKGOST3410Signer$gost3410");
         // put("Alg.Alias.Signature.GOST-3410", "GOST3410");
         // put("Alg.Alias.Signature.GOST-3410-94", "GOST3410");
@@ -985,7 +1116,7 @@
     //     String jdk11Variation1 = digest + "with" + algorithm;
     //     String jdk11Variation2 = digest + "With" + algorithm;
     //     String alias = digest + "/" + algorithm;
-
+    //
     //     put("Signature." + mainName, className);
     //     put("Alg.Alias.Signature." + jdk11Variation1, mainName);
     //     put("Alg.Alias.Signature." + jdk11Variation2, mainName);
@@ -994,4 +1125,9 @@
     //     put("Alg.Alias.Signature.OID." + oid, mainName);
     // }
     // END android-removed
+
+    public void setParameter(String parameterName, Object parameter)
+    {
+        ProviderUtil.setParameter(parameterName, parameter);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java b/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java
deleted file mode 100644
index a1350b2..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java
+++ /dev/null
@@ -1,624 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-import javax.crypto.spec.RC5ParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.engines.DESEngine;
-import org.bouncycastle.crypto.engines.DESedeEngine;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.engines.TwofishEngine;
-// END android-removed
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.modes.CFBBlockCipher;
-import org.bouncycastle.crypto.modes.CTSBlockCipher;
-import org.bouncycastle.crypto.modes.OFBBlockCipher;
-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.params.RC5Parameters;
-import org.bouncycastle.util.Strings;
-
-public class BrokenJCEBlockCipher
-    implements BrokenPBE
-{
-    //
-    // specs we can handle.
-    //
-    private Class[]                 availableSpecs =
-                                    {
-                                        IvParameterSpec.class,
-                                        PBEParameterSpec.class,
-                                        RC2ParameterSpec.class,
-                                        RC5ParameterSpec.class
-                                    };
- 
-    private BufferedBlockCipher     cipher;
-    private ParametersWithIV        ivParam;
-
-    private int                     pbeType = PKCS12;
-    private int                     pbeHash = SHA1;
-    private int                     pbeKeySize;
-    private int                     pbeIvSize;
-
-    private int                     ivLength = 0;
-
-    private AlgorithmParameters     engineParams = null;
-
-    protected BrokenJCEBlockCipher(
-        BlockCipher engine)
-    {
-        cipher = new PaddedBufferedBlockCipher(engine);
-    }
-        
-    protected BrokenJCEBlockCipher(
-        BlockCipher engine,
-        int         pbeType,
-        int         pbeHash,
-        int         pbeKeySize,
-        int         pbeIvSize)
-    {
-        cipher = new PaddedBufferedBlockCipher(engine);
-
-        this.pbeType = pbeType;
-        this.pbeHash = pbeHash;
-        this.pbeKeySize = pbeKeySize;
-        this.pbeIvSize = pbeIvSize;
-    }
-
-    protected int engineGetBlockSize() 
-    {
-        return cipher.getBlockSize();
-    }
-
-    protected byte[] engineGetIV() 
-    {
-        return (ivParam != null) ? ivParam.getIV() : null;
-    }
-
-    protected int engineGetKeySize(
-        Key     key) 
-    {
-        return key.getEncoded().length;
-    }
-
-    protected int engineGetOutputSize(
-        int     inputLen) 
-    {
-        return cipher.getOutputSize(inputLen);
-    }
-
-    protected AlgorithmParameters engineGetParameters() 
-    {
-        if (engineParams == null)
-        {
-            if (ivParam != null)
-            {
-                String  name = cipher.getUnderlyingCipher().getAlgorithmName();
-
-                if (name.indexOf('/') >= 0)
-                {
-                    name = name.substring(0, name.indexOf('/'));
-                }
-
-                try
-                {
-                    engineParams = AlgorithmParameters.getInstance(name, "BC");
-                    engineParams.init(ivParam.getIV());
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException(e.toString());
-                }
-            }
-        }
-
-        return engineParams;
-    }
-
-    protected void engineSetMode(
-        String  mode) 
-    {
-        String  modeName = Strings.toUpperCase(mode);
-
-        if (modeName.equals("ECB"))
-        {
-            ivLength = 0;
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher());
-        }
-        else if (modeName.equals("CBC"))
-        {
-            ivLength = cipher.getUnderlyingCipher().getBlockSize();
-            cipher = new PaddedBufferedBlockCipher(
-                            new CBCBlockCipher(cipher.getUnderlyingCipher()));
-        }
-        else if (modeName.startsWith("OFB"))
-        {
-            ivLength = cipher.getUnderlyingCipher().getBlockSize();
-            if (modeName.length() != 3)
-            {
-                int wordSize = Integer.parseInt(modeName.substring(3));
-
-                cipher = new PaddedBufferedBlockCipher(
-                                new OFBBlockCipher(cipher.getUnderlyingCipher(), wordSize));
-            }
-            else
-            {
-                cipher = new PaddedBufferedBlockCipher(
-                        new OFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize()));
-            }
-        }
-        else if (modeName.startsWith("CFB"))
-        {
-            ivLength = cipher.getUnderlyingCipher().getBlockSize();
-            if (modeName.length() != 3)
-            {
-                int wordSize = Integer.parseInt(modeName.substring(3));
-
-                cipher = new PaddedBufferedBlockCipher(
-                                new CFBBlockCipher(cipher.getUnderlyingCipher(), wordSize));
-            }
-            else
-            {
-                cipher = new PaddedBufferedBlockCipher(
-                        new CFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize()));
-            }
-        }
-        else
-        {
-            throw new IllegalArgumentException("can't support mode " + mode);
-        }
-    }
-
-    protected void engineSetPadding(
-        String  padding) 
-    throws NoSuchPaddingException
-    {
-        String  paddingName = Strings.toUpperCase(padding);
-
-        if (paddingName.equals("NOPADDING"))
-        {
-            cipher = new BufferedBlockCipher(cipher.getUnderlyingCipher());
-        }
-        else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING") || paddingName.equals("ISO10126PADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher());
-        }
-        else if (paddingName.equals("WITHCTS"))
-        {
-            cipher = new CTSBlockCipher(cipher.getUnderlyingCipher());
-        }
-        else
-        {
-            throw new NoSuchPaddingException("Padding " + padding + " unknown.");
-        }
-    }
-
-    protected void engineInit(
-        int                     opmode,
-        Key                     key,
-        AlgorithmParameterSpec  params,
-        SecureRandom            random) 
-    throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        CipherParameters        param;
-
-        //
-        // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it).
-        //
-        if (key instanceof JCEPBEKey)
-        {
-            param = BrokenPBE.Util.makePBEParameters((JCEPBEKey)key, params, pbeType, pbeHash,
-                        cipher.getUnderlyingCipher().getAlgorithmName(), pbeKeySize, pbeIvSize);
-
-            if (pbeIvSize != 0)
-            {
-                ivParam = (ParametersWithIV)param;
-            }
-        }
-        else if (params == null)
-        {
-            param = new KeyParameter(key.getEncoded());
-        }
-        else if (params instanceof IvParameterSpec)
-        {
-            if (ivLength != 0)
-            {
-                param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
-                ivParam = (ParametersWithIV)param;
-            }
-            else
-            {
-                param = new KeyParameter(key.getEncoded());
-            }
-        }
-        else if (params instanceof RC2ParameterSpec)
-        {
-            RC2ParameterSpec    rc2Param = (RC2ParameterSpec)params;
-
-            param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits());
-
-            if (rc2Param.getIV() != null && ivLength != 0)
-            {
-                param = new ParametersWithIV(param, rc2Param.getIV());
-                ivParam = (ParametersWithIV)param;
-            }
-        }
-        else if (params instanceof RC5ParameterSpec)
-        {
-            RC5ParameterSpec    rc5Param = (RC5ParameterSpec)params;
-
-            param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds());
-            if (rc5Param.getWordSize() != 32)
-            {
-                throw new IllegalArgumentException("can only accept RC5 word size 32 (at the moment...)");
-            }
-            if ((rc5Param.getIV() != null) && (ivLength != 0))
-            {
-                param = new ParametersWithIV(param, rc5Param.getIV());
-                ivParam = (ParametersWithIV)param;
-            }
-        }
-        else
-        {
-            throw new InvalidAlgorithmParameterException("unknown parameter type.");
-        }
-
-        if ((ivLength != 0) && !(param instanceof ParametersWithIV))
-        {
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
-            {
-                byte[]  iv = new byte[ivLength];
-
-                random.nextBytes(iv);
-                param = new ParametersWithIV(param, iv);
-                ivParam = (ParametersWithIV)param;
-            }
-            else
-            {
-                throw new InvalidAlgorithmParameterException("no IV set when one expected");
-            }
-        }
-
-        switch (opmode)
-        {
-        case Cipher.ENCRYPT_MODE:
-        case Cipher.WRAP_MODE:
-            cipher.init(true, param);
-            break;
-        case Cipher.DECRYPT_MODE:
-        case Cipher.UNWRAP_MODE:
-            cipher.init(false, param);
-            break;
-        default:
-            System.out.println("eeek!");
-        }
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        AlgorithmParameters params,
-        SecureRandom        random) 
-    throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        AlgorithmParameterSpec  paramSpec = null;
-
-        if (params != null)
-        {
-            for (int i = 0; i != availableSpecs.length; i++)
-            {
-                try
-                {
-                    paramSpec = params.getParameterSpec(availableSpecs[i]);
-                    break;
-                }
-                catch (Exception e)
-                {
-                    continue;
-                }
-            }
-
-            if (paramSpec == null)
-            {
-                throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
-            }
-        }
-
-        engineParams = params;
-        engineInit(opmode, key, paramSpec, random);
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        SecureRandom        random) 
-        throws InvalidKeyException
-    {
-        try
-        {
-            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
-        }
-        catch (InvalidAlgorithmParameterException e)
-        {
-            throw new IllegalArgumentException(e.getMessage());
-        }
-    }
-
-    protected byte[] engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-    {
-        int     length = cipher.getUpdateOutputSize(inputLen);
-
-        if (length > 0)
-        {
-                byte[]  out = new byte[length];
-
-                cipher.processBytes(input, inputOffset, inputLen, out, 0);
-                return out;
-        }
-
-        cipher.processBytes(input, inputOffset, inputLen, null, 0);
-
-        return null;
-    }
-
-    protected int engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-    {
-        return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
-    }
-
-    protected byte[] engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-        throws IllegalBlockSizeException, BadPaddingException
-    {
-        int     len = 0;
-        byte[]  tmp = new byte[engineGetOutputSize(inputLen)];
-
-        if (inputLen != 0)
-        {
-            len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0);
-        }
-
-        try
-        {
-            len += cipher.doFinal(tmp, len);
-        }
-        catch (DataLengthException e)
-        {
-            throw new IllegalBlockSizeException(e.getMessage());
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadPaddingException(e.getMessage());
-        }
-
-        byte[]  out = new byte[len];
-
-        System.arraycopy(tmp, 0, out, 0, len);
-
-        return out;
-    }
-
-    protected int engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-        throws IllegalBlockSizeException, BadPaddingException
-    {
-        int     len = 0;
-
-        if (inputLen != 0)
-        {
-                len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
-        }
-
-        try
-        {
-            return len + cipher.doFinal(output, outputOffset + len);
-        }
-        catch (DataLengthException e)
-        {
-            throw new IllegalBlockSizeException(e.getMessage());
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadPaddingException(e.getMessage());
-        }
-    }
-
-    protected byte[] engineWrap(
-        Key     key) 
-    throws IllegalBlockSizeException, java.security.InvalidKeyException
-    {
-        byte[] encoded = key.getEncoded();
-        if (encoded == null)
-        {
-            throw new InvalidKeyException("Cannot wrap key, null encoding.");
-        }
-
-        try
-        {
-            return engineDoFinal(encoded, 0, encoded.length);
-        }
-        catch (BadPaddingException e)
-        {
-            throw new IllegalBlockSizeException(e.getMessage());
-        }
-    }
-
-    protected Key engineUnwrap(
-        byte[]  wrappedKey,
-        String  wrappedKeyAlgorithm,
-        int     wrappedKeyType) 
-    throws InvalidKeyException
-    {
-        byte[] encoded = null;
-        try
-        {
-            encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
-        }
-        catch (BadPaddingException e)
-        {
-            throw new InvalidKeyException(e.getMessage());
-        }
-        catch (IllegalBlockSizeException e2)
-        {
-            throw new InvalidKeyException(e2.getMessage());
-        }
-
-        if (wrappedKeyType == Cipher.SECRET_KEY)
-        {
-            return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
-        }
-        else
-        {
-            try
-            {
-                KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, "BC");
-
-                if (wrappedKeyType == Cipher.PUBLIC_KEY)
-                {
-                    return kf.generatePublic(new X509EncodedKeySpec(encoded));
-                }
-                else if (wrappedKeyType == Cipher.PRIVATE_KEY)
-                {
-                    return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
-                }
-            }
-            catch (NoSuchProviderException e)
-            {
-                throw new InvalidKeyException("Unknown key type " + e.getMessage());
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new InvalidKeyException("Unknown key type " + e.getMessage());
-            }
-            catch (InvalidKeySpecException e2)
-            {
-                throw new InvalidKeyException("Unknown key type " + e2.getMessage());
-            }
-
-            throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
-        }
-    }
-
-    /*
-     * The ciphers that inherit from us.
-     */
-
-    /**
-     * PBEWithMD5AndDES
-     */
-    static public class BrokePBEWithMD5AndDES
-        extends BrokenJCEBlockCipher
-    {
-        public BrokePBEWithMD5AndDES()
-        {
-            super(new CBCBlockCipher(new DESEngine()), PKCS5S1, MD5, 64, 64);
-        }
-    }
-
-    /**
-     * PBEWithSHA1AndDES
-     */
-    static public class BrokePBEWithSHA1AndDES
-        extends BrokenJCEBlockCipher
-    {
-        public BrokePBEWithSHA1AndDES()
-        {
-            super(new CBCBlockCipher(new DESEngine()), PKCS5S1, SHA1, 64, 64);
-        }
-    }
-
-    /**
-     * PBEWithSHAAnd3-KeyTripleDES-CBC
-     */
-    static public class BrokePBEWithSHAAndDES3Key
-        extends BrokenJCEBlockCipher
-    {
-        public BrokePBEWithSHAAndDES3Key()
-        {
-            super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 192, 64);
-        }
-    }
-
-    /**
-     * OldPBEWithSHAAnd3-KeyTripleDES-CBC
-     */
-    static public class OldPBEWithSHAAndDES3Key
-        extends BrokenJCEBlockCipher
-    {
-        public OldPBEWithSHAAndDES3Key()
-        {
-            super(new CBCBlockCipher(new DESedeEngine()), OLD_PKCS12, SHA1, 192, 64);
-        }
-    }
-
-    /**
-     * PBEWithSHAAnd2-KeyTripleDES-CBC
-     */
-    static public class BrokePBEWithSHAAndDES2Key
-        extends BrokenJCEBlockCipher
-    {
-        public BrokePBEWithSHAAndDES2Key()
-        {
-            super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 128, 64);
-        }
-    }
-
-    /**
-     * OldPBEWithSHAAndTwofish-CBC
-     */
-// BEGIN android-removed
-//    static public class OldPBEWithSHAAndTwofish
-//        extends BrokenJCEBlockCipher
-//    {
-//        public OldPBEWithSHAAndTwofish()
-//        {
-//            super(new CBCBlockCipher(new TwofishEngine()), OLD_PKCS12, SHA1, 256, 128);
-//        }
-//    }
-// END android-removed
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java b/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java
deleted file mode 100644
index e6186f6..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
-import org.bouncycastle.crypto.DerivationParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.params.KDFParameters;
-
-/**
- * Generator for PBE derived keys and ivs as defined by IEEE P1363a
- * <br>
- * This implementation is based on draft 9 of IEEE P1363a. <b>Note:</b>
- * as this is still a draft the output of this generator may change, don't
- * use it for anything that might be subject to long term storage.
- */
-public class BrokenKDF2BytesGenerator
-    implements DerivationFunction
-{
-    private Digest  digest;
-    private byte[]  shared;
-    private byte[]  iv;
-
-    /**
-     * Construct a KDF2 Parameters generator. Generates key material
-     * according to IEEE P1363a - if you want orthodox results you should
-     * use a digest specified in the standard.
-     * <p>
-     * <b>Note:</b> IEEE P1363a standard is still a draft standard, if the standard
-     * changes this function, the output of this function will change as well.
-     * Don't use this routine for anything subject to long term storage.
-     *
-     * @param digest the digest to be used as the source of derived keys.
-     */
-    public BrokenKDF2BytesGenerator(
-        Digest  digest)
-    {
-        this.digest = digest;
-    }
-
-    public void init(
-        DerivationParameters    param)
-    {
-        if (!(param instanceof KDFParameters))
-        {
-            throw new IllegalArgumentException("KDF parameters required for KDF2Generator");
-        }
-
-        KDFParameters   p = (KDFParameters)param;
-
-        shared = p.getSharedSecret();
-        iv = p.getIV();
-    }
-
-    /**
-     * return the underlying digest.
-     */
-    public Digest getDigest()
-    {
-        return digest;
-    }
-
-    /**
-     * fill len bytes of the output buffer with bytes generated from
-     * the derivation function.
-     *
-     * @throws IllegalArgumentException if the size of the request will cause an overflow.
-     * @throws DataLengthException if the out buffer is too small.
-     */
-    public int generateBytes(
-        byte[]  out,
-        int     outOff,
-        int     len)
-        throws DataLengthException, IllegalArgumentException
-    {
-        if ((out.length - len) < outOff)
-        {
-            throw new DataLengthException("output buffer too small");
-        }
-
-        long    oBits = len * 8;
-
-        //
-        // this is at odds with the standard implementation, the
-        // maximum value should be hBits * (2^23 - 1) where hBits
-        // is the digest output size in bits. We can't have an
-        // array with a long index at the moment...
-        //
-        if (oBits > (digest.getDigestSize() * 8 * (2L^32 - 1)))
-        {
-            new IllegalArgumentException("Output length to large");
-        }
-    
-        int cThreshold = (int)(oBits / digest.getDigestSize());
-
-        byte[] dig = null;
-
-        dig = new byte[digest.getDigestSize()];
-
-        for (int counter = 1; counter <= cThreshold; counter++)
-        {
-            digest.update(shared, 0, shared.length);
-
-            digest.update((byte)(counter & 0xff));
-            digest.update((byte)((counter >> 8) & 0xff));
-            digest.update((byte)((counter >> 16) & 0xff));
-            digest.update((byte)((counter >> 24) & 0xff));
-
-            digest.update(iv, 0, iv.length);
-
-            digest.doFinal(dig, 0);
-
-            if ((len - outOff) > dig.length)
-            {
-                System.arraycopy(dig, 0, out, outOff, dig.length);
-                outOff += dig.length;
-            }
-            else
-            {
-                System.arraycopy(dig, 0, out, outOff, len - outOff);
-            }
-        }
-    
-        digest.reset();
-
-        return len;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java b/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java
deleted file mode 100644
index 6ac4f49..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java
+++ /dev/null
@@ -1,448 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.spec.PBEParameterSpec;
-
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.PBEParametersGenerator;
-import org.bouncycastle.crypto.digests.MD5Digest;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-// END android-removed
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
-import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
-import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-
-/**
- * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0,
- * with a bug affecting 180 bit plus keys - this class is only here to
- * allow smooth migration of the version 0 keystore to version 1. Don't
- * use it (it won't be staying around).
- * <p>
- * The document this implementation is based on can be found at
- * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html>
- * RSA's PKCS12 Page</a>
- */
-class OldPKCS12ParametersGenerator
-    extends PBEParametersGenerator
-{
-    public static final int KEY_MATERIAL = 1;
-    public static final int IV_MATERIAL  = 2;
-    public static final int MAC_MATERIAL = 3;
-
-    private Digest digest;
-
-    private int     u;
-    private int     v;
-
-    /**
-     * Construct a PKCS 12 Parameters generator. This constructor will
-     * accept MD5, SHA1, and RIPEMD160.
-     *
-     * @param digest the digest to be used as the source of derived keys.
-     * @exception IllegalArgumentException if an unknown digest is passed in.
-     */
-    public OldPKCS12ParametersGenerator(
-        Digest  digest)
-    {
-        this.digest = digest;
-        if (digest instanceof MD5Digest)
-        {
-            u = 128 / 8;
-            v = 512 / 8;
-        }
-        else if (digest instanceof SHA1Digest)
-        {
-            u = 160 / 8;
-            v = 512 / 8;
-        }
-        // BEGIN android-removed
-        // else if (digest instanceof RIPEMD160Digest)
-        // {
-        //     u = 160 / 8;
-        //     v = 512 / 8;
-        // }
-        // END android-removed
-        else
-        {
-            throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported");
-        }
-    }
-
-    /**
-     * add a + b + 1, returning the result in a. The a value is treated
-     * as a BigInteger of length (b.length * 8) bits. The result is 
-     * modulo 2^b.length in case of overflow.
-     */
-    private void adjust(
-        byte[]  a,
-        int     aOff,
-        byte[]  b)
-    {
-        int  x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1;
-
-        a[aOff + b.length - 1] = (byte)x;
-        x >>>= 8;
-
-        for (int i = b.length - 2; i >= 0; i--)
-        {
-            x += (b[i] & 0xff) + (a[aOff + i] & 0xff);
-            a[aOff + i] = (byte)x;
-            x >>>= 8;
-        }
-    }
-
-    /**
-     * generation of a derived key ala PKCS12 V1.0.
-     */
-    private byte[] generateDerivedKey(
-        int idByte,
-        int n)
-    {
-        byte[]  D = new byte[v];
-        byte[]  dKey = new byte[n];
-
-        for (int i = 0; i != D.length; i++)
-        {
-            D[i] = (byte)idByte;
-        }
-
-        byte[]  S;
-
-        if ((salt != null) && (salt.length != 0))
-        {
-            S = new byte[v * ((salt.length + v - 1) / v)];
-
-            for (int i = 0; i != S.length; i++)
-            {
-                S[i] = salt[i % salt.length];
-            }
-        }
-        else
-        {
-            S = new byte[0];
-        }
-
-        byte[]  P;
-
-        if ((password != null) && (password.length != 0))
-        {
-            P = new byte[v * ((password.length + v - 1) / v)];
-
-            for (int i = 0; i != P.length; i++)
-            {
-                P[i] = password[i % password.length];
-            }
-        }
-        else
-        {
-            P = new byte[0];
-        }
-
-        byte[]  I = new byte[S.length + P.length];
-
-        System.arraycopy(S, 0, I, 0, S.length);
-        System.arraycopy(P, 0, I, S.length, P.length);
-
-        byte[]  B = new byte[v];
-        int     c = (n + u - 1) / u;
-
-        for (int i = 1; i <= c; i++)
-        {
-            byte[]  A = new byte[u];
-
-            digest.update(D, 0, D.length);
-            digest.update(I, 0, I.length);
-            digest.doFinal(A, 0);
-            for (int j = 1; j != iterationCount; j++)
-            {
-                digest.update(A, 0, A.length);
-                digest.doFinal(A, 0);
-            }
-
-            for (int j = 0; j != B.length; j++)
-            {
-                B[i] = A[j % A.length];
-            }
-
-            for (int j = 0; j != I.length / v; j++)
-            {
-                adjust(I, j * v, B);
-            }
-
-            if (i == c)
-            {
-                System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u));
-            }
-            else
-            {
-                System.arraycopy(A, 0, dKey, (i - 1) * u, A.length);
-            }
-        }
-
-        return dKey;
-    }
-
-    /**
-     * Generate a key parameter derived from the password, salt, and iteration
-     * count we are currently initialised with.
-     *
-     * @param keySize the size of the key we want (in bits)
-     * @return a KeyParameter object.
-     */
-    public CipherParameters generateDerivedParameters(
-        int keySize)
-    {
-        keySize = keySize / 8;
-
-        byte[]  dKey = generateDerivedKey(KEY_MATERIAL, keySize);
-
-        return new KeyParameter(dKey, 0, keySize);
-    }
-
-    /**
-     * Generate a key with initialisation vector parameter derived from
-     * the password, salt, and iteration count we are currently initialised
-     * with.
-     *
-     * @param keySize the size of the key we want (in bits)
-     * @param ivSize the size of the iv we want (in bits)
-     * @return a ParametersWithIV object.
-     */
-    public CipherParameters generateDerivedParameters(
-        int     keySize,
-        int     ivSize)
-    {
-        keySize = keySize / 8;
-        ivSize = ivSize / 8;
-
-        byte[]  dKey = generateDerivedKey(KEY_MATERIAL, keySize);
-
-        byte[]  iv = generateDerivedKey(IV_MATERIAL, ivSize);
-
-        return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize);
-    }
-
-    /**
-     * Generate a key parameter for use with a MAC derived from the password,
-     * salt, and iteration count we are currently initialised with.
-     *
-     * @param keySize the size of the key we want (in bits)
-     * @return a KeyParameter object.
-     */
-    public CipherParameters generateDerivedMacParameters(
-        int keySize)
-    {
-        keySize = keySize / 8;
-
-        byte[]  dKey = generateDerivedKey(MAC_MATERIAL, keySize);
-
-        return new KeyParameter(dKey, 0, keySize);
-    }
-}
-
-public interface BrokenPBE
-{
-    //
-    // PBE Based encryption constants - by default we do PKCS12 with SHA-1
-    //
-    static final int        MD5         = 0;
-    static final int        SHA1        = 1;
-    static final int        RIPEMD160   = 2;
-
-    static final int        PKCS5S1     = 0;
-    static final int        PKCS5S2     = 1;
-    static final int        PKCS12      = 2;
-    static final int        OLD_PKCS12  = 3;
-
-    /**
-     * uses the appropriate mixer to generate the key and IV if neccessary.
-     */
-    static class Util
-    {
-        /**
-         * a faulty parity routine...
-         *
-         * @param bytes the byte array to set the parity on.
-         */
-        static private void setOddParity(
-            byte[] bytes)
-        {
-            for (int i = 0; i < bytes.length; i++)
-            {
-                int b = bytes[i];
-                bytes[i] = (byte)((b & 0xfe) |
-                                (((b >> 1) ^
-                                (b >> 2) ^
-                                (b >> 3) ^
-                                (b >> 4) ^
-                                (b >> 5) ^
-                                (b >> 6) ^
-                                (b >> 7)) ^ 0x01));
-            }
-        }
-
-        static private PBEParametersGenerator makePBEGenerator(
-            int                     type,
-            int                     hash)
-        {
-            PBEParametersGenerator  generator;
-    
-            if (type == PKCS5S1)
-            {
-                switch (hash)
-                {
-                case MD5:
-                    generator = new PKCS5S1ParametersGenerator(new MD5Digest());
-                    break;
-                case SHA1:
-                    generator = new PKCS5S1ParametersGenerator(new SHA1Digest());
-                    break;
-                default:
-                    throw new IllegalStateException("PKCS5 scheme 1 only supports only MD5 and SHA1.");
-                }
-            }
-            else if (type == PKCS5S2)
-            {
-                generator = new PKCS5S2ParametersGenerator();
-            }
-            else if (type == OLD_PKCS12)
-            {
-                switch (hash)
-                {
-                case MD5:
-                    generator = new OldPKCS12ParametersGenerator(new MD5Digest());
-                    break;
-                case SHA1:
-                    generator = new OldPKCS12ParametersGenerator(new SHA1Digest());
-                    break;
-                // BEGIN android-removed
-                // case RIPEMD160:
-                //     generator = new OldPKCS12ParametersGenerator(new RIPEMD160Digest());
-                //     break;
-                // END android-removed
-                default:
-                    throw new IllegalStateException("unknown digest scheme for PBE encryption.");
-                }
-            }
-            else
-            {
-                switch (hash)
-                {
-                case MD5:
-                    generator = new PKCS12ParametersGenerator(new MD5Digest());
-                    break;
-                case SHA1:
-                    generator = new PKCS12ParametersGenerator(new SHA1Digest());
-                    break;
-                // BEGIN android-removed
-                // case RIPEMD160:
-                //     generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
-                //     break;
-                // END android-removed
-                default:
-                    throw new IllegalStateException("unknown digest scheme for PBE encryption.");
-                }
-            }
-    
-            return generator;
-        }
-
-        /**
-         * construct a key and iv (if neccessary) suitable for use with a 
-         * Cipher.
-         */
-        static CipherParameters makePBEParameters(
-            JCEPBEKey               pbeKey,
-            AlgorithmParameterSpec  spec,
-            int                     type,
-            int                     hash,
-            String                  targetAlgorithm,
-            int                     keySize,
-            int                     ivSize)
-        {
-            if ((spec == null) || !(spec instanceof PBEParameterSpec))
-            {
-                throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
-            }
-    
-            PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
-            PBEParametersGenerator  generator = makePBEGenerator(type, hash);
-            byte[]                  key = pbeKey.getEncoded();
-            CipherParameters        param;
-    
-            generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
-
-            if (ivSize != 0)
-            {
-                param = generator.generateDerivedParameters(keySize, ivSize);
-            }
-            else
-            {
-                param = generator.generateDerivedParameters(keySize);
-            }
-
-            if (targetAlgorithm.startsWith("DES"))
-            {
-                if (param instanceof ParametersWithIV)
-                {
-                    KeyParameter    kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
-
-                    setOddParity(kParam.getKey());
-                }
-                else
-                {
-                    KeyParameter    kParam = (KeyParameter)param;
-
-                    setOddParity(kParam.getKey());
-                }
-            }
-
-            for (int i = 0; i != key.length; i++)
-            {
-                key[i] = 0;
-            }
-
-            return param;
-        }
-
-        /**
-         * generate a PBE based key suitable for a MAC algorithm, the
-         * key size is chosen according the MAC size, or the hashing algorithm,
-         * whichever is greater.
-         */
-        static CipherParameters makePBEMacParameters(
-            JCEPBEKey               pbeKey,
-            AlgorithmParameterSpec  spec,
-            int                     type,
-            int                     hash,
-            int                     keySize)
-        {
-            if ((spec == null) || !(spec instanceof PBEParameterSpec))
-            {
-                throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
-            }
-    
-            PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
-            PBEParametersGenerator  generator = makePBEGenerator(type, hash);
-            byte[]                  key = pbeKey.getEncoded();
-            CipherParameters        param;
-    
-            generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
-
-            param = generator.generateDerivedMacParameters(keySize);
-    
-            for (int i = 0; i != key.length; i++)
-            {
-                key[i] = 0;
-            }
-
-            return param;
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
index 8383b98..d675024 100644
--- a/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+++ b/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -2,13 +2,16 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.PublicKey;
-import java.security.cert.CRL;
 import java.security.cert.CertPath;
 import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertSelector;
 import java.security.cert.CertStore;
 import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.PKIXParameters;
 import java.security.cert.PolicyQualifierInfo;
 import java.security.cert.TrustAnchor;
@@ -16,6 +19,10 @@
 import java.security.cert.X509CRLSelector;
 import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -29,20 +36,52 @@
 
 import javax.security.auth.x500.X500Principal;
 
+// BEGIN android-added
+import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters;
+
+// END android-added
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1OutputStream;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.PolicyInformation;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509Extensions;
+// BEGIN android-removed
+// import org.bouncycastle.jce.X509LDAPCertStoreParameters;
+// END android-removed
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.StoreException;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+// BEGIN android-removed
+// import org.bouncycastle.x509.X509AttributeCertStoreSelector;
+// END android-removed
+import org.bouncycastle.x509.X509AttributeCertificate;
+import org.bouncycastle.x509.X509CRLStoreSelector;
+import org.bouncycastle.x509.X509CertStoreSelector;
+import org.bouncycastle.x509.X509Store;
 
 public class CertPathValidatorUtilities
 {
-
     protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId();
     protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId();
     protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId();
@@ -53,7 +92,9 @@
     protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId();
     protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId();
     protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId();
-    
+    protected static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId();
+    protected static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId();
+    protected static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId();
 
     protected static final String ANY_POLICY = "2.5.29.32.0";
     
@@ -78,135 +119,214 @@
         "privilegeWithdrawn",
         "aACompromise" };
     
-// BEGIN android-changed
+    // BEGIN android-removed
+    // /**
+    //  * Search the given Set of TrustAnchor's for one that is the
+    //  * issuer of the given X509 certificate. Uses the default provider
+    //  * for signature verification.
+    //  *
+    //  * @param cert the X509 certificate
+    //  * @param trustAnchors a Set of TrustAnchor's
+    //  *
+    //  * @return the <code>TrustAnchor</code> object if found or
+    //  * <code>null</code> if not.
+    //  *
+    //  * @exception AnnotatedException
+    //  *                if a TrustAnchor was found but the signature verification
+    //  *                on the given certificate has thrown an exception.
+    //  */
+    // protected static TrustAnchor findTrustAnchor(
+    //     X509Certificate cert,
+    //     Set             trustAnchors)
+    //         throws AnnotatedException
+    // {
+    //     return findTrustAnchor(cert, trustAnchors, null);
+    // }
+    // END android-removed
+    
+    // BEGIN android-changed
     /**
      * Search the given Set of TrustAnchor's for one that is the
-     * issuer of the given X509 certificate.
+     * issuer of the given X509 certificate. Uses the specified
+     * provider for signature verification, or the default provider
+     * if null.
      *
      * @param cert the X509 certificate
-     * @param params with trust anchors
+     * @param params used to find the trust anchors and signature provider
      *
      * @return the <code>TrustAnchor</code> object if found or
      * <code>null</code> if not.
      *
-     * @exception CertPathValidatorException if a TrustAnchor  was
-     * found but the signature verification on the given certificate
-     * has thrown an exception. This Exception can be obtainted with
-     * <code>getCause()</code> method.
-     **/
-    static final TrustAnchor findTrustAnchor(
-            X509Certificate cert,
-            CertPath certPath,
-            int index,
-            PKIXParameters params)
-            throws CertPathValidatorException {
+     * @exception AnnotatedException
+     *                if a TrustAnchor was found but the signature verification
+     *                on the given certificate has thrown an exception.
+     */
+    protected static TrustAnchor findTrustAnchor(
+        X509Certificate cert,
+        PKIXParameters  params)
+            throws AnnotatedException
+    // END android-changed
+    {
+        // BEGIN android-changed
         // If we have a trust anchor index, use it.
         if (params instanceof IndexedPKIXParameters) {
-            IndexedPKIXParameters indexed = (IndexedPKIXParameters) params;
-            return indexed.findTrustAnchor(cert, certPath, index);
+            try {
+                IndexedPKIXParameters indexed = (IndexedPKIXParameters) params;
+                return indexed.findTrustAnchor(cert);
+            } catch (CertPathValidatorException e) {
+                throw new AnnotatedException(e.getMessage(), e);
+            }
         }
-
-        Iterator iter = params.getTrustAnchors().iterator();
-        TrustAnchor found = null;
+        // END android-changed
+        TrustAnchor trust = null;
         PublicKey trustPublicKey = null;
         Exception invalidKeyEx = null;
 
         X509CertSelector certSelectX509 = new X509CertSelector();
+        X500Principal certIssuer = getEncodedIssuerPrincipal(cert);
 
         try
         {
-            certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded());
+            certSelectX509.setSubject(certIssuer.getEncoded());
         }
         catch (IOException ex)
         {
-            throw new CertPathValidatorException(ex);
+            throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex);
         }
 
+        // BEGIN android-changed
+        Iterator iter = params.getTrustAnchors().iterator();
+        // END android-changed
+        // BEGIN android-added
         byte[] certBytes = null;
         try {
             certBytes = cert.getEncoded();
         } catch (Exception e) {
             // ignore, just continue
         }
-        while (iter.hasNext() && found == null)
+        // END android-added
+        while (iter.hasNext() && trust == null)
         {
-            found = (TrustAnchor) iter.next();
-            X509Certificate foundCert = found.getTrustedCert();
-            if (foundCert != null)
-            {
-                // If the trust anchor is identical to the certificate we're
-                // done. Just return the anchor.
-                // There is similar code in PKIXCertPathValidatorSpi.
-                try {
-                    byte[] foundBytes = foundCert.getEncoded();
-                    if (certBytes != null && Arrays.equals(foundBytes,
-                            certBytes)) {
-                        return found;
-                    }
-                } catch (Exception e) {
-                    // ignore, continue and verify the certificate
+            trust = (TrustAnchor) iter.next();
+            // BEGIN android-changed
+            X509Certificate trustCert = trust.getTrustedCert();
+            // END android-changed
+            // BEGIN android-added
+            // If the trust anchor is identical to the certificate we're
+            // done. Just return the anchor.
+            // There is similar code in PKIXCertPathValidatorSpi.
+            try {
+                byte[] trustBytes = trustCert.getEncoded();
+                if (certBytes != null && Arrays.equals(trustBytes, certBytes)) {
+                    return trust;
                 }
-                if (certSelectX509.match(foundCert))
+            } catch (Exception e) {
+                // ignore, continue and verify the certificate
+            }
+            // END android-added
+            // BEGIN android-changed
+            if (trustCert != null)
+            {
+                if (certSelectX509.match(trustCert))
                 {
-                    trustPublicKey = foundCert.getPublicKey();
+                    trustPublicKey = trustCert.getPublicKey();
                 }
                 else
                 {
-                    found = null;
+                    trust = null;
                 }
             }
-            else if (found.getCAName() != null
-                    && found.getCAPublicKey() != null)
+            // END android-changed
+            else if (trust.getCAName() != null
+                    && trust.getCAPublicKey() != null)
             {
                 try
                 {
-                    X500Principal certIssuer = getEncodedIssuerPrincipal(cert);
-                    X500Principal caName = new X500Principal(found.getCAName());
+                    X500Principal caName = new X500Principal(trust.getCAName());
                     if (certIssuer.equals(caName))
                     {
-                        trustPublicKey = found.getCAPublicKey();
+                        trustPublicKey = trust.getCAPublicKey();
                     }
                     else
                     {
-                        found = null;
+                        trust = null;
                     }
                 }
                 catch (IllegalArgumentException ex)
                 {
-                    found = null;
+                    trust = null;
                 }
             }
             else
             {
-                found = null;
+                trust = null;
             }
 
             if (trustPublicKey != null)
             {
                 try
                 {
-                    cert.verify(trustPublicKey);
+                    // BEGIN android-changed
+                    verifyX509Certificate(cert, trustPublicKey, params.getSigProvider());
+                    // END android-changed
                 }
                 catch (Exception ex)
                 {
                     invalidKeyEx = ex;
-                    found = null;
+                    trust = null;
                 }
             }
         }
 
-        if (found == null && invalidKeyEx != null)
+        if (trust == null && invalidKeyEx != null)
         {
-            throw new CertPathValidatorException("TrustAnchor found but certificate validation failed.", invalidKeyEx, certPath, index);
+            throw new AnnotatedException("TrustAnchor found but certificate validation failed.", invalidKeyEx);
         }
 
-        return found;
+        return trust;
     }
-// END android-changed
 
-    protected static X500Principal getEncodedIssuerPrincipal(X509Certificate cert)
+    protected static void addAdditionalStoresFromAltNames(
+            X509Certificate cert,
+            ExtendedPKIXParameters pkixParams)
+            throws CertificateParsingException
     {
-        return cert.getIssuerX500Principal();
+        // if in the IssuerAltName extension an URI
+        // is given, add an additinal X.509 store
+        if (cert.getIssuerAlternativeNames() != null)
+        {
+            Iterator it = cert.getIssuerAlternativeNames().iterator();
+            while (it.hasNext())
+            {
+                // look for URI
+                List list = (List) it.next();
+                // BEGIN android-changed
+                if (list.get(0).equals(Integer.valueOf(GeneralName.uniformResourceIdentifier)))
+                // END android-changed
+                {
+                    // found
+                    String temp = (String) list.get(1);
+                    CertPathValidatorUtilities.addAdditionalStoreFromLocation(temp, pkixParams);
+                }
+            }
+        }
+    }
+    /**
+     * Returns the issuer of an attribute certificate or certificate.
+     * @param cert The attribute certificate or certificate.
+     * @return The issuer as <code>X500Principal</code>.
+     */
+    protected static X500Principal getEncodedIssuerPrincipal(
+        Object cert)
+    {
+        if (cert instanceof X509Certificate)
+        {
+            return ((X509Certificate)cert).getIssuerX500Principal();
+        }
+        else
+        {
+            return (X500Principal)((X509AttributeCertificate)cert).getIssuer().getPrincipals()[0];
+        }
     }
 
     protected static Date getValidDate(PKIXParameters paramsPKIX)
@@ -233,7 +353,14 @@
     
     
     /**
-     * extract the value of the given extension, if it exists.
+     * Extract the value of the given extension, if it exists.
+     * 
+     * @param ext
+     *            The extension object.
+     * @param oid
+     *            The object identifier to obtain.
+     * @throws AnnotatedException
+     *             if the extension cannot be read.
      */
     protected static DERObject getExtensionValue(
         java.security.cert.X509Extension    ext,
@@ -262,7 +389,7 @@
             aIn = new ASN1InputStream(octs.getOctets());
             return aIn.readObject();
         }
-        catch (IOException e)
+        catch (Exception e)
         {
             throw new AnnotatedException("exception processing extension " + oid, e);
         }
@@ -285,344 +412,78 @@
 
             return info.getAlgorithmId();
         }
-        catch (IOException e)
+        catch (Exception e)
         {
-            throw new CertPathValidatorException("exception processing public key");
+            throw new ExtCertPathValidatorException("Subject public key cannot be decoded.", e);
         }
     }
-
-    //
-    // Utility functions for name constraint checking
-    //
-
-    private static boolean withinDNSubtree(ASN1Sequence dns, ASN1Sequence subtree)
-    {
-        if (subtree.size() < 1)
-        {
-            return false;
-        }
-
-        if (subtree.size() > dns.size())
-        {
-            return false;
-        }
-
-        for (int j = subtree.size() - 1; j >= 0; j--)
-        {
-            if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j)))
-            {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    protected static void checkPermittedDN(Set permitted, ASN1Sequence dns)
-            throws CertPathValidatorException
-    {
-        if (permitted.isEmpty())
-        {
-            return;
-        }
-
-        Iterator it = permitted.iterator();
-
-        while (it.hasNext())
-        {
-            ASN1Sequence subtree = (ASN1Sequence) it.next();
-
-            if (withinDNSubtree(dns, subtree))
-            {
-                return;
-            }
-        }
-
-        throw new CertPathValidatorException(
-                "Subject distinguished name is not from a permitted subtree");
-    }
-
-    protected static void checkExcludedDN(Set excluded, ASN1Sequence dns)
-            throws CertPathValidatorException
-    {
-        if (excluded.isEmpty())
-        {
-            return;
-        }
-
-        Iterator it = excluded.iterator();
-
-        while (it.hasNext())
-        {
-            ASN1Sequence subtree = (ASN1Sequence) it.next();
-
-            if (withinDNSubtree(dns, subtree))
-            {
-                throw new CertPathValidatorException(
-                        "Subject distinguished name is from an excluded subtree");
-            }
-        }
-    }
-
-    protected static Set intersectDN(Set permitted, ASN1Sequence dn)
-    {
-        if (permitted.isEmpty())
-        {
-            permitted.add(dn);
-
-            return permitted;
-        }
-        else
-        {
-            Set intersect = new HashSet();
-
-            Iterator _iter = permitted.iterator();
-            while (_iter.hasNext())
-            {
-                ASN1Sequence subtree = (ASN1Sequence) _iter.next();
-
-                if (withinDNSubtree(dn, subtree))
-                {
-                    intersect.add(dn);
-                }
-                else if (withinDNSubtree(subtree, dn))
-                {
-                    intersect.add(subtree);
-                }
-            }
-
-            return intersect;
-        }
-    }
-
-    protected static Set unionDN(Set excluded, ASN1Sequence dn)
-    {
-        if (excluded.isEmpty())
-        {
-            excluded.add(dn);
-
-            return excluded;
-        }
-        else
-        {
-            Set intersect = new HashSet();
-
-            Iterator _iter = excluded.iterator();
-            while (_iter.hasNext())
-            {
-                ASN1Sequence subtree = (ASN1Sequence) _iter.next();
-
-                if (withinDNSubtree(dn, subtree))
-                {
-                    intersect.add(subtree);
-                }
-                else if (withinDNSubtree(subtree, dn))
-                {
-                    intersect.add(dn);
-                }
-                else
-                {
-                    intersect.add(subtree);
-                    intersect.add(dn);
-                }
-            }
-
-            return intersect;
-        }
-    }
-
-    protected static Set intersectEmail(Set permitted, String email)
-    {
-        String _sub = email.substring(email.indexOf('@') + 1);
-
-        if (permitted.isEmpty())
-        {
-            permitted.add(_sub);
-
-            return permitted;
-        }
-        else
-        {
-            Set intersect = new HashSet();
-
-            Iterator _iter = permitted.iterator();
-            while (_iter.hasNext())
-            {
-                String _permitted = (String) _iter.next();
-
-                if (_sub.endsWith(_permitted))
-                {
-                    intersect.add(_sub);
-                }
-                else if (_permitted.endsWith(_sub))
-                {
-                    intersect.add(_permitted);
-                }
-            }
-
-            return intersect;
-        }
-    }
-
-    protected static Set unionEmail(Set excluded, String email)
-    {
-        String _sub = email.substring(email.indexOf('@') + 1);
-
-        if (excluded.isEmpty())
-        {
-            excluded.add(_sub);
-            return excluded;
-        }
-        else
-        {
-            Set intersect = new HashSet();
-
-            Iterator _iter = excluded.iterator();
-            while (_iter.hasNext())
-            {
-                String _excluded = (String) _iter.next();
-
-                if (_sub.endsWith(_excluded))
-                {
-                    intersect.add(_excluded);
-                }
-                else if (_excluded.endsWith(_sub))
-                {
-                    intersect.add(_sub);
-                }
-                else
-                {
-                    intersect.add(_excluded);
-                    intersect.add(_sub);
-                }
-            }
-
-            return intersect;
-        }
-    }
-
-    protected static Set intersectIP(Set permitted, byte[] ip)
-    {
-        // TBD
-        return permitted;
-    }
-
-    protected static Set unionIP(Set excluded, byte[] ip)
-    {
-        // TBD
-        return excluded;
-    }
-
-    protected static void checkPermittedEmail(Set permitted, String email)
-            throws CertPathValidatorException
-    {
-        if (permitted.isEmpty())
-        {
-            return;
-        }
-
-        String sub = email.substring(email.indexOf('@') + 1);
-        Iterator it = permitted.iterator();
-
-        while (it.hasNext())
-        {
-            String str = (String) it.next();
-
-            if (sub.endsWith(str))
-            {
-                return;
-            }
-        }
-
-        throw new CertPathValidatorException(
-                "Subject email address is not from a permitted subtree");
-    }
-
-    protected static void checkExcludedEmail(Set excluded, String email)
-            throws CertPathValidatorException
-    {
-        if (excluded.isEmpty())
-        {
-            return;
-        }
-
-        String sub = email.substring(email.indexOf('@') + 1);
-        Iterator it = excluded.iterator();
-
-        while (it.hasNext())
-        {
-            String str = (String) it.next();
-            if (sub.endsWith(str))
-            {
-                throw new CertPathValidatorException(
-                        "Subject email address is from an excluded subtree");
-            }
-        }
-    }
-
-    protected static void checkPermittedIP(Set permitted, byte[] ip)
-            throws CertPathValidatorException
-    {
-        if (permitted.isEmpty())
-        {
-            return;
-        }
-
-        // TODO: ??? Something here
-    }
-
-    protected static void checkExcludedIP(Set excluded, byte[] ip)
-            throws CertPathValidatorException
-    {
-        if (excluded.isEmpty())
-        {
-            return;
-        }
-
-        // TODO, check RFC791 and RFC1883 for IP bytes definition.
-    }
-    
     
     // crl checking
-    
+
     /**
-     * Return a Collection of all CRLs found in the
-     * CertStore's that are matching the crlSelect criteriums.
+     * Return a Collection of all CRLs found in the X509Store's that are
+     * matching the crlSelect criteriums.
      *
-     * @param crlSelect a {@link CertSelector CertSelector}
-     * object that will be used to select the CRLs
-     * @param crlStores a List containing only {@link CertStore
-     * CertStore} objects. These are used to search for
-     * CRLs
+     * @param crlSelect a {@link X509CRLStoreSelector} object that will be used
+     *            to select the CRLs
+     * @param crlStores a List containing only
+     *            {@link org.bouncycastle.x509.X509Store  X509Store} objects.
+     *            These are used to search for CRLs
      *
-     * @return a Collection of all found {@link CRL CRL}
-     * objects. May be empty but never <code>null</code>.
+     * @return a Collection of all found {@link X509CRL X509CRL} objects. May be
+     *         empty but never <code>null</code>.
      */
-    protected static final Collection findCRLs(
-        X509CRLSelector crlSelect,
-        List            crlStores)
-        throws AnnotatedException
+    protected static final Collection findCRLs(X509CRLStoreSelector crlSelect,
+        List crlStores) throws AnnotatedException
     {
         Set crls = new HashSet();
         Iterator iter = crlStores.iterator();
-    
+
+        AnnotatedException lastException = null;
+        boolean foundValidStore = false;
+
         while (iter.hasNext())
         {
-            CertStore   certStore = (CertStore)iter.next();
-    
-            try
+            Object obj = iter.next();
+
+            if (obj instanceof X509Store)
             {
-                crls.addAll(certStore.getCRLs(crlSelect));
+                X509Store store = (X509Store)obj;
+
+                try
+                {
+                    crls.addAll(store.getMatches(crlSelect));
+                    foundValidStore = true;
+                }
+                catch (StoreException e)
+                {
+                    lastException = new AnnotatedException(
+                        "Exception searching in X.509 CRL store.", e);
+                }
             }
-            catch (CertStoreException e)
+            else
             {
-                throw new AnnotatedException("cannot extract crl: " + e, e);
+                CertStore store = (CertStore)obj;
+
+                try
+                {
+                    crls.addAll(store.getCRLs(crlSelect));
+                    foundValidStore = true;
+                }
+                catch (CertStoreException e)
+                {
+                    lastException = new AnnotatedException(
+                        "Exception searching in X.509 CRL store.", e);
+                }
             }
         }
-    
+        if (!foundValidStore && lastException != null)
+        {
+            throw lastException;
+        }
         return crls;
     }
-    
+
     //
     // policy checking
     // 
@@ -652,7 +513,7 @@
             }
             catch (IOException ex)
             {
-                throw new CertPathValidatorException("exception building qualifier set: " + ex);
+                throw new ExtCertPathValidatorException("Policy qualifier info cannot be decoded.", ex);
             }
     
             bOut.reset();
@@ -755,8 +616,7 @@
         for (int j = 0; j < policyNodeVec.size(); j++)
         {
             PKIXPolicyNode _node = (PKIXPolicyNode)policyNodeVec.get(j);
-            Set            _expectedPolicies = _node.getExpectedPolicies();
-            
+
             if (ANY_POLICY.equals(_node.getValidPolicy()))
             {
                 Set _childExpectedPolicies = new HashSet();
@@ -806,14 +666,41 @@
                 if (ANY_POLICY.equals(node.getValidPolicy()))
                 {
                     Set pq = null;
-                    ASN1Sequence policies = (ASN1Sequence)getExtensionValue(cert, CERTIFICATE_POLICIES);
+                    ASN1Sequence policies = null;
+                    try
+                    {
+                        policies = DERSequence.getInstance(getExtensionValue(cert, CERTIFICATE_POLICIES));
+                    }
+                    catch (Exception e)
+                    {
+                        throw
+
+                        new AnnotatedException("Certificate policies cannot be decoded.", e);
+                    }
                     Enumeration e = policies.getObjects();
                     while (e.hasMoreElements())
                     {
-                        PolicyInformation pinfo = PolicyInformation.getInstance(e.nextElement());
+                        PolicyInformation pinfo = null;
+
+                        try
+                        {
+                            pinfo = PolicyInformation.getInstance(e.nextElement());
+                        }
+                        catch (Exception ex)
+                        {
+                            throw new AnnotatedException("Policy information cannot be decoded.", ex);
+                        }
                         if (ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId()))
                         {
+                            try
+                            {
                             pq = getQualifierSet(pinfo.getPolicyQualifiers());
+                            }
+                            catch (CertPathValidatorException ex)
+                            {
+                                throw new ExtCertPathValidatorException(
+                                        "Policy qualifier info set could not be built.", ex);
+                            }
                             break;
                         }
                     }
@@ -881,4 +768,791 @@
         return policySet == null || policySet.contains(ANY_POLICY) || policySet.isEmpty();
     }
     
+    protected static void addAdditionalStoreFromLocation(String location,
+        ExtendedPKIXParameters pkixParams)
+    {
+        if (pkixParams.isAdditionalLocationsEnabled())
+        {
+            try
+            {
+                // BEGIN android-removed
+                // if (location.startsWith("ldap://"))
+                // {
+                //     // ldap://directory.d-trust.net/CN=D-TRUST
+                //     // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
+                //     // skip "ldap://"
+                //     location = location.substring(7);
+                //     // after first / baseDN starts
+                //     String base = null;
+                //     String url = null;
+                //     if (location.indexOf("/") != -1)
+                //     {
+                //         base = location.substring(location.indexOf("/"));
+                //         // URL
+                //         url = "ldap://"
+                //             + location.substring(0, location.indexOf("/"));
+                //     }
+                //     else
+                //     {
+                //         url = "ldap://" + location;
+                //     }
+                //     // use all purpose parameters
+                //     X509LDAPCertStoreParameters params = new X509LDAPCertStoreParameters.Builder(
+                //         url, base).build();
+                //     pkixParams.addAdditionalStore(X509Store.getInstance(
+                //         "CERTIFICATE/LDAP", params, "BC"));
+                //     pkixParams.addAdditionalStore(X509Store.getInstance(
+                //         "CRL/LDAP", params, "BC"));
+                //     pkixParams.addAdditionalStore(X509Store.getInstance(
+                //         "ATTRIBUTECERTIFICATE/LDAP", params, "BC"));
+                //     pkixParams.addAdditionalStore(X509Store.getInstance(
+                //         "CERTIFICATEPAIR/LDAP", params, "BC"));
+                // }
+                // END android-removed
+            }
+            catch (Exception e)
+            {
+                // cannot happen
+                throw new RuntimeException("Exception adding X.509 stores.");
+            }
+        }
+    }
+
+    /**
+     * Return a Collection of all certificates or attribute certificates found
+     * in the X509Store's that are matching the certSelect criteriums.
+     *
+     * @param certSelect a {@link Selector} object that will be used to select
+     *            the certificates
+     * @param certStores a List containing only {@link X509Store} objects. These
+     *            are used to search for certificates.
+     *
+     * @return a Collection of all found {@link X509Certificate} or
+     *         {@link org.bouncycastle.x509.X509AttributeCertificate} objects.
+     *         May be empty but never <code>null</code>.
+     */
+    protected static Collection findCertificates(X509CertStoreSelector certSelect,
+        List certStores) throws AnnotatedException
+    {
+        Set certs = new HashSet();
+        Iterator iter = certStores.iterator();
+
+        while (iter.hasNext())
+        {
+            Object obj = iter.next();
+
+            if (obj instanceof X509Store)
+            {
+                X509Store certStore = (X509Store)obj;
+                try
+                {
+                    certs.addAll(certStore.getMatches(certSelect));
+                }
+                catch (StoreException e)
+                {
+                    throw
+
+                    new AnnotatedException(
+                        "Problem while picking certificates from X.509 store.", e);
+                }
+            }
+            else
+            {
+                CertStore certStore = (CertStore)obj;
+
+                try
+                {
+                    certs.addAll(certStore.getCertificates(certSelect));
+                }
+                catch (CertStoreException e)
+                {
+                    throw new AnnotatedException(
+                        "Problem while picking certificates from certificate store.",
+                        e);
+                }
+            }
+        }
+        return certs;
+    }
+
+    // BEGIN android-removed
+    // protected static Collection findCertificates(X509AttributeCertStoreSelector certSelect,
+    //                                              List certStores)
+    // throws AnnotatedException
+    // {
+    //     Set certs = new HashSet();
+    //     Iterator iter = certStores.iterator();
+    //
+    //     while (iter.hasNext())
+    //     {
+    //         Object obj = iter.next();
+    //
+    //         if (obj instanceof X509Store)
+    //         {
+    //             X509Store certStore = (X509Store)obj;
+    //             try
+    //             {
+    //                 certs.addAll(certStore.getMatches(certSelect));
+    //             }
+    //             catch (StoreException e)
+    //             {
+    //                 throw
+    //
+    //                     new AnnotatedException(
+    //                         "Problem while picking certificates from X.509 store.", e);
+    //             }
+    //         }
+    //     }
+    //     return certs;
+    // }
+    // END android-removed
+
+    protected static void addAdditionalStoresFromCRLDistributionPoint(
+        CRLDistPoint crldp, ExtendedPKIXParameters pkixParams)
+        throws AnnotatedException
+    {
+        if (crldp != null)
+        {
+            DistributionPoint dps[] = null;
+            try
+            {
+                dps = crldp.getDistributionPoints();
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "Distribution points could not be read.", e);
+            }
+            for (int i = 0; i < dps.length; i++)
+            {
+                DistributionPointName dpn = dps[i].getDistributionPoint();
+                // look for URIs in fullName
+                if (dpn != null)
+                {
+                    if (dpn.getType() == DistributionPointName.FULL_NAME)
+                    {
+                        GeneralName[] genNames = GeneralNames.getInstance(
+                            dpn.getName()).getNames();
+                        // look for an URI
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier)
+                            {
+                                String location = DERIA5String.getInstance(
+                                    genNames[j].getName()).getString();
+                                CertPathValidatorUtilities
+                                    .addAdditionalStoreFromLocation(location,
+                                        pkixParams);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Add the CRL issuers from the cRLIssuer field of the distribution point or
+     * from the certificate if not given to the issuer criterion of the
+     * <code>selector</code>.
+     * <p>
+     * The <code>issuerPrincipals</code> are a collection with a single
+     * <code>X500Principal</code> for <code>X509Certificate</code>s. For
+     * {@link X509AttributeCertificate}s the issuer may contain more than one
+     * <code>X500Principal</code>.
+     *
+     * @param dp The distribution point.
+     * @param issuerPrincipals The issuers of the certificate or attribute
+     *            certificate which contains the distribution point.
+     * @param selector The CRL selector.
+     * @param pkixParams The PKIX parameters containing the cert stores.
+     * @throws AnnotatedException if an exception occurs while processing.
+     * @throws ClassCastException if <code>issuerPrincipals</code> does not
+     * contain only <code>X500Principal</code>s.
+     */
+    protected static void getCRLIssuersFromDistributionPoint(
+        DistributionPoint dp,
+        Collection issuerPrincipals,
+        X509CRLSelector selector,
+        ExtendedPKIXParameters pkixParams)
+        throws AnnotatedException
+    {
+        List issuers = new ArrayList();
+        // indirect CRL
+        if (dp.getCRLIssuer() != null)
+        {
+            GeneralName genNames[] = dp.getCRLIssuer().getNames();
+            // look for a DN
+            for (int j = 0; j < genNames.length; j++)
+            {
+                if (genNames[j].getTagNo() == GeneralName.directoryName)
+                {
+                    try
+                    {
+                        issuers.add(new X500Principal(genNames[j].getName()
+                            .getDERObject().getEncoded()));
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "CRL issuer information from distribution point cannot be decoded.",
+                            e);
+                    }
+                }
+            }
+        }
+        else
+        {
+            /*
+             * certificate issuer is CRL issuer, distributionPoint field MUST be
+             * present.
+             */
+            if (dp.getDistributionPoint() == null)
+            {
+                throw new AnnotatedException(
+                    "CRL issuer is omitted from distribution point but no distributionPoint field present.");
+            }
+            // add and check issuer principals
+            for (Iterator it=issuerPrincipals.iterator(); it.hasNext();)
+            {
+                issuers.add((X500Principal)it.next());
+            }
+        }
+        // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
+        // distributionPoint
+//        if (dp.getDistributionPoint() != null)
+//        {
+//            // look for nameRelativeToCRLIssuer
+//            if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+//            {
+//                // append fragment to issuer, only one
+//                // issuer can be there, if this is given
+//                if (issuers.size() != 1)
+//                {
+//                    throw new AnnotatedException(
+//                        "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
+//                }
+//                DEREncodable relName = dp.getDistributionPoint().getName();
+//                Iterator it = issuers.iterator();
+//                List issuersTemp = new ArrayList(issuers.size());
+//                while (it.hasNext())
+//                {
+//                    Enumeration e = null;
+//                    try
+//                    {
+//                        e = ASN1Sequence.getInstance(
+//                            new ASN1InputStream(((X500Principal) it.next())
+//                                .getEncoded()).readObject()).getObjects();
+//                    }
+//                    catch (IOException ex)
+//                    {
+//                        throw new AnnotatedException(
+//                            "Cannot decode CRL issuer information.", ex);
+//                    }
+//                    ASN1EncodableVector v = new ASN1EncodableVector();
+//                    while (e.hasMoreElements())
+//                    {
+//                        v.add((DEREncodable) e.nextElement());
+//                    }
+//                    v.add(relName);
+//                    issuersTemp.add(new X500Principal(new DERSequence(v)
+//                        .getDEREncoded()));
+//                }
+//                issuers.clear();
+//                issuers.addAll(issuersTemp);
+//            }
+//        }
+        Iterator it = issuers.iterator();
+        while (it.hasNext())
+        {
+            try
+            {
+                selector.addIssuerName(((X500Principal)it.next()).getEncoded());
+            }
+            catch (IOException ex)
+            {
+                throw new AnnotatedException(
+                    "Cannot decode CRL issuer information.", ex);
+            }
+        }
+    }
+
+    private static BigInteger getSerialNumber(
+            Object cert)
+    {
+        if (cert instanceof X509Certificate)
+        {
+            return ((X509Certificate) cert).getSerialNumber();
+        }
+        else
+        {
+            return ((X509AttributeCertificate) cert).getSerialNumber();
+        }
+    }
+    
+    protected static void getCertStatus(
+            Date validDate,
+            X509CRL crl,
+            Object cert,
+            CertStatus certStatus)
+        throws AnnotatedException
+    {
+        // use BC X509CRLObject so that indirect CRLs are supported
+        X509CRLObject bcCRL = null;
+        try
+        {
+            bcCRL = new X509CRLObject(new CertificateList((ASN1Sequence) ASN1Sequence.fromByteArray(crl.getEncoded())));
+        }
+        catch (Exception exception)
+        {
+            throw new AnnotatedException("Bouncy Castle X509CRLObject could not be created.", exception);
+        }
+        // use BC X509CRLEntryObject, so that getCertificateIssuer() is
+        // supported.
+        X509CRLEntryObject crl_entry = (X509CRLEntryObject) bcCRL.getRevokedCertificate(getSerialNumber(cert));
+        if (crl_entry != null
+                && (getEncodedIssuerPrincipal(cert).equals(crl_entry.getCertificateIssuer()) || getEncodedIssuerPrincipal(cert)
+                        .equals(getIssuerPrincipal(crl))))
+        {
+            DEREnumerated reasonCode = null;
+            if (crl_entry.hasExtensions())
+            {
+                try
+                {
+                    reasonCode = DEREnumerated
+                        .getInstance(CertPathValidatorUtilities
+                            .getExtensionValue(crl_entry,
+                                X509Extensions.ReasonCode.getId()));
+                }
+                catch (Exception e)
+                {
+                    new AnnotatedException(
+                        "Reason code CRL entry extension could not be decoded.",
+                        e);
+                }
+            }
+
+            // for reason keyCompromise, caCompromise, aACompromise or
+            // unspecified
+            if (!(validDate.getTime() < crl_entry.getRevocationDate().getTime())
+                || reasonCode == null
+                || reasonCode.getValue().intValue() == 0
+                || reasonCode.getValue().intValue() == 1
+                || reasonCode.getValue().intValue() == 2
+                || reasonCode.getValue().intValue() == 8)
+            {
+
+                // (i) or (j) (1)
+                if (reasonCode != null)
+                {
+                    certStatus.setCertStatus(reasonCode.getValue().intValue());
+                }
+                // (i) or (j) (2)
+                else
+                {
+                    certStatus.setCertStatus(CRLReason.unspecified);
+                }
+                certStatus.setRevocationDate(crl_entry.getRevocationDate());
+            }
+        }
+    }
+
+    /**
+     * Fetches delta CRLs according to RFC 3280 section 5.2.4.
+     *
+     * @param currentDate The date for which the delta CRLs must be valid.
+     * @param paramsPKIX The extended PKIX parameters.
+     * @param completeCRL The complete CRL the delta CRL is for.
+     * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
+     * @throws AnnotatedException if an exception occurs while picking the delta
+     *             CRLs.
+     */
+    protected static Set getDeltaCRLs(Date currentDate,
+        ExtendedPKIXParameters paramsPKIX, X509CRL completeCRL)
+        throws AnnotatedException
+    {
+
+        X509CRLStoreSelector deltaSelect = new X509CRLStoreSelector();
+
+        if (paramsPKIX.getDate() != null)
+        {
+            deltaSelect.setDateAndTime(paramsPKIX.getDate());
+        }
+        else
+        {
+            deltaSelect.setDateAndTime(currentDate);
+        }
+
+        // 5.2.4 (a)
+        try
+        {
+            deltaSelect.addIssuerName(CertPathValidatorUtilities
+                .getIssuerPrincipal(completeCRL).getEncoded());
+        }
+        catch (IOException e)
+        {
+            new AnnotatedException("Cannot extract issuer from CRL.", e);
+        }
+
+        BigInteger completeCRLNumber = null;
+        try
+        {
+            DERObject derObject = CertPathValidatorUtilities.getExtensionValue(completeCRL,
+                    CRL_NUMBER);
+            if (derObject != null)
+            {
+                completeCRLNumber = CRLNumber.getInstance(derObject).getPositiveValue();
+            }
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException(
+                "CRL number extension could not be extracted from CRL.", e);
+        }
+
+        // 5.2.4 (b)
+        byte[] idp = null;
+        try
+        {
+            idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException(
+                "Issuing distribution point extension value could not be read.",
+                e);
+        }
+
+        // 5.2.4 (d)
+
+        deltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
+            .add(BigInteger.valueOf(1)));
+
+        deltaSelect.setIssuingDistributionPoint(idp);
+        deltaSelect.setIssuingDistributionPointEnabled(true);
+
+        // 5.2.4 (c)
+        deltaSelect.setMaxBaseCRLNumber(completeCRLNumber);
+
+        Set temp = new HashSet();
+        // find delta CRLs
+        try
+        {
+            temp.addAll(CertPathValidatorUtilities.findCRLs(deltaSelect, paramsPKIX.getAdditionalStores()));
+            temp.addAll(CertPathValidatorUtilities.findCRLs(deltaSelect, paramsPKIX.getStores()));
+            temp.addAll(CertPathValidatorUtilities.findCRLs(deltaSelect, paramsPKIX.getCertStores()));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Could not search for delta CRLs.", e);
+        }
+
+        Set result = new HashSet();
+
+        for (Iterator it = temp.iterator(); it.hasNext();)
+        {
+            X509CRL crl = (X509CRL)it.next();
+            
+            if (isDeltaCRL(crl))
+            {
+                result.add(crl);
+            }
+        }
+
+        return result;
+    }
+
+    private static boolean isDeltaCRL(X509CRL crl)
+    {
+        Set critical = crl.getCriticalExtensionOIDs();
+
+        return critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+    }
+
+    /**
+     * Fetches complete CRLs according to RFC 3280.
+     *
+     * @param dp The distribution point for which the complete CRL
+     * @param cert The <code>X509Certificate</code> or
+     *            {@link org.bouncycastle.x509.X509AttributeCertificate} for
+     *            which the CRL should be searched.
+     * @param currentDate The date for which the delta CRLs must be valid.
+     * @param paramsPKIX The extended PKIX parameters.
+     * @return A <code>Set</code> of <code>X509CRL</code>s with complete
+     *         CRLs.
+     * @throws AnnotatedException if an exception occurs while picking the CRLs
+     *             or no CRLs are found.
+     */
+    protected static Set getCompleteCRLs(DistributionPoint dp, Object cert,
+        Date currentDate, ExtendedPKIXParameters paramsPKIX)
+        throws AnnotatedException
+    {
+        X509CRLStoreSelector crlselect = new X509CRLStoreSelector();
+        try
+        {
+            Set issuers = new HashSet();
+            if (cert instanceof X509AttributeCertificate)
+            {
+                issuers.add(((X509AttributeCertificate)cert)
+                    .getIssuer().getPrincipals()[0]);
+            }
+            else
+            {
+                issuers.add(getEncodedIssuerPrincipal(cert));
+            }
+            CertPathValidatorUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, crlselect, paramsPKIX);
+        }
+        catch (AnnotatedException e)
+        {
+            new AnnotatedException(
+                "Could not get issuer information from distribution point.", e);
+        }
+        if (cert instanceof X509Certificate)
+        {
+            crlselect.setCertificateChecking((X509Certificate)cert);
+        }
+        else if (cert instanceof X509AttributeCertificate)
+        {
+            crlselect.setAttrCertificateChecking((X509AttributeCertificate)cert);
+        }
+
+        if (paramsPKIX.getDate() != null)
+        {
+            crlselect.setDateAndTime(paramsPKIX.getDate());
+        }
+        else
+        {
+            crlselect.setDateAndTime(currentDate);
+        }
+
+        crlselect.setCompleteCRLEnabled(true);
+
+        Set crls = new HashSet();
+        try
+        {
+            crls.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getStores()));
+            crls.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getAdditionalStores()));
+            crls.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getCertStores()));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Could not search for CRLs.", e);
+        }
+        if (crls.isEmpty())
+        {
+            if (cert instanceof X509AttributeCertificate)
+            {
+                X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
+
+                throw new AnnotatedException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
+            }
+            else
+            {
+                X509Certificate xCert = (X509Certificate)cert;
+
+                throw new AnnotatedException("No CRLs found for issuer \"" + xCert.getIssuerX500Principal() + "\"");
+            }
+        }
+        return crls;
+    }
+
+    protected static Date getValidCertDateFromValidityModel(
+        ExtendedPKIXParameters paramsPKIX, CertPath certPath, int index)
+        throws AnnotatedException
+    {
+        if (paramsPKIX.getValidityModel() == ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL)
+        {
+            // if end cert use given signing/encryption/... time
+            if (index <= 0)
+            {
+                return CertPathValidatorUtilities.getValidDate(paramsPKIX);
+                // else use time when previous cert was created
+            }
+            else
+            {
+                if (index - 1 == 0)
+                {
+                    DERGeneralizedTime dateOfCertgen = null;
+                    try
+                    {
+                        byte[] extBytes = ((X509Certificate)certPath.getCertificates().get(index - 1)).getExtensionValue(ISISMTTObjectIdentifiers.id_isismtt_at_dateOfCertGen.getId());
+                        if (extBytes != null)
+                        {
+                            dateOfCertgen = DERGeneralizedTime.getInstance(ASN1Object.fromByteArray(extBytes));
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "Date of cert gen extension could not be read.");
+                    }
+                    catch (IllegalArgumentException e)
+                    {
+                        throw new AnnotatedException(
+                            "Date of cert gen extension could not be read.");
+                    }
+                    if (dateOfCertgen != null)
+                    {
+                        try
+                        {
+                            return dateOfCertgen.getDate();
+                        }
+                        catch (ParseException e)
+                        {
+                            throw new AnnotatedException(
+                                "Date from date of cert gen extension could not be parsed.",
+                                e);
+                        }
+                    }
+                    return ((X509Certificate) certPath.getCertificates().get(
+                        index - 1)).getNotBefore();
+                }
+                else
+                {
+                    return ((X509Certificate) certPath.getCertificates().get(
+                        index - 1)).getNotBefore();
+                }
+            }
+        }
+        else
+        {
+            return getValidDate(paramsPKIX);
+        }
+    }
+
+    /**
+     * Return the next working key inheriting DSA parameters if necessary.
+     * <p>
+     * This methods inherits DSA parameters from the indexed certificate or
+     * previous certificates in the certificate chain to the returned
+     * <code>PublicKey</code>. The list is searched upwards, meaning the end
+     * certificate is at position 0 and previous certificates are following.
+     * </p>
+     * <p>
+     * If the indexed certificate does not contain a DSA key this method simply
+     * returns the public key. If the DSA key already contains DSA parameters
+     * the key is also only returned.
+     * </p>
+     * 
+     * @param certs The certification path.
+     * @param index The index of the certificate which contains the public key
+     *            which should be extended with DSA parameters.
+     * @return The public key of the certificate in list position
+     *         <code>index</code> extended with DSA parameters if applicable.
+     * @throws AnnotatedException if DSA parameters cannot be inherited.
+     */
+    protected static PublicKey getNextWorkingKey(List certs, int index)
+        throws CertPathValidatorException
+    {
+        Certificate cert = (Certificate) certs.get(index);
+        PublicKey pubKey = cert.getPublicKey();
+        if (!(pubKey instanceof DSAPublicKey))
+        {
+            return pubKey;
+        }
+        DSAPublicKey dsaPubKey = (DSAPublicKey) pubKey;
+        if (dsaPubKey.getParams() != null)
+        {
+            return dsaPubKey;
+        }
+        for (int i = index + 1; i < certs.size(); i++)
+        {
+            X509Certificate parentCert = (X509Certificate)certs.get(i);
+            pubKey = parentCert.getPublicKey();
+            if (!(pubKey instanceof DSAPublicKey))
+            {
+                throw new CertPathValidatorException(
+                    "DSA parameters cannot be inherited from previous certificate.");
+            }
+            DSAPublicKey prevDSAPubKey = (DSAPublicKey) pubKey;
+            if (prevDSAPubKey.getParams() == null)
+            {
+                continue;
+            }
+            DSAParams dsaParams = prevDSAPubKey.getParams();
+            DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(
+                dsaPubKey.getY(), dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
+            try
+            {
+                KeyFactory keyFactory = KeyFactory.getInstance("DSA", "BC");
+                return keyFactory.generatePublic(dsaPubKeySpec);
+            }
+            catch (Exception exception)
+            {
+                throw new RuntimeException(exception.getMessage());
+            }
+        }
+        throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
+    }
+    
+    /**
+     * Find the issuer certificates of a given certificate.
+     * 
+     * @param cert
+     *            The certificate for which an issuer should be found.
+     * @param pkixParams
+     * @return A <code>Collection</code> object containing the issuer
+     *         <code>X509Certificate</code>s. Never <code>null</code>.
+     * 
+     * @exception AnnotatedException
+     *                if an error occurs.
+     */
+    protected static Collection findIssuerCerts(
+        X509Certificate cert,
+        ExtendedPKIXBuilderParameters pkixParams)
+            throws AnnotatedException
+    {
+        X509CertStoreSelector certSelect = new X509CertStoreSelector();
+        Set certs = new HashSet();
+        try
+        {
+            certSelect.setSubject(cert.getIssuerX500Principal().getEncoded());
+        }
+        catch (IOException ex)
+        {
+            throw new AnnotatedException(
+                    "Subject criteria for certificate selector to find issuer certificate could not be set.", ex);
+        }
+
+        Iterator iter;
+
+        try
+        {
+            List matches = new ArrayList();
+
+            matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getCertStores()));
+            matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getStores()));
+            matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getAdditionalStores()));
+
+            iter = matches.iterator();
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Issuer certificate cannot be searched.", e);
+        }
+
+        X509Certificate issuer = null;
+        while (iter.hasNext())
+        {
+            issuer = (X509Certificate) iter.next();
+            // issuer cannot be verified because possible DSA inheritance
+            // parameters are missing
+            certs.add(issuer);
+        }
+        return certs;
+    }
+
+    protected static void verifyX509Certificate(X509Certificate cert, PublicKey publicKey,
+        String sigProvider)
+            throws GeneralSecurityException
+    {
+        if (sigProvider == null)
+        {
+            cert.verify(publicKey);
+        }
+        else
+        {
+            cert.verify(publicKey, sigProvider);
+        }
+    }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/CertStatus.java b/src/main/java/org/bouncycastle/jce/provider/CertStatus.java
new file mode 100644
index 0000000..ba3da16
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/CertStatus.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.jce.provider;
+
+import java.util.Date;
+
+class CertStatus
+{
+    public static final int UNREVOKED = 11;
+
+    public static final int UNDETERMINED = 12;
+
+    int certStatus = UNREVOKED;
+
+    Date revocationDate = null;
+
+    /**
+     * @return Returns the revocationDate.
+     */
+    public Date getRevocationDate()
+    {
+        return revocationDate;
+    }
+
+    /**
+     * @param revocationDate The revocationDate to set.
+     */
+    public void setRevocationDate(Date revocationDate)
+    {
+        this.revocationDate = revocationDate;
+    }
+
+    /**
+     * @return Returns the certStatus.
+     */
+    public int getCertStatus()
+    {
+        return certStatus;
+    }
+
+    /**
+     * @param certStatus The certStatus to set.
+     */
+    public void setCertStatus(int certStatus)
+    {
+        this.certStatus = certStatus;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java b/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java
index e1ac37c..210d986 100644
--- a/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java
+++ b/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java
@@ -9,10 +9,10 @@
 import java.security.cert.CertStoreSpi;
 import java.security.cert.Certificate;
 import java.security.cert.CollectionCertStoreParameters;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
 
 public class CertStoreCollectionSpi extends CertStoreSpi
 {
@@ -35,7 +35,7 @@
         CertSelector selector)
         throws CertStoreException 
     {
-        Set         col = new HashSet();
+        List        col = new ArrayList();
         Iterator    iter = params.getCollection().iterator();
 
         if (selector == null)
@@ -71,7 +71,7 @@
         CRLSelector selector)
         throws CertStoreException 
     {
-        Set         col = new HashSet();
+        List        col = new ArrayList();
         Iterator    iter = params.getCollection().iterator();
 
         if (selector == null)
diff --git a/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java b/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java
new file mode 100644
index 0000000..3bc820f
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.cert.CRLException;
+
+class ExtCRLException
+    extends CRLException
+{
+    Throwable cause;
+
+    ExtCRLException(String message, Throwable cause)
+    {
+        super(message);
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java b/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java
deleted file mode 100644
index 03679e2..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.bouncycastle.jce.provider;
-
-import javax.security.auth.x500.X500Principal;
-
-import java.security.cert.CertPath;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.PKIXParameters;
-import java.security.cert.TrustAnchor;
-import java.security.cert.X509Certificate;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyStoreException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
-import java.util.logging.Level;
-
-/**
- * Indexes trust anchors so they can be found in O(1) time instead of O(N).
- */
-public class IndexedPKIXParameters extends PKIXParameters {
-
-    final Map<Bytes, TrustAnchor> encodings
-            = new HashMap<Bytes, TrustAnchor>();
-    final Map<X500Principal, TrustAnchor> bySubject
-            = new HashMap<X500Principal, TrustAnchor>();
-    final Map<X500Principal, List<TrustAnchor>> byCA
-            = new HashMap<X500Principal, List<TrustAnchor>>();
-
-    public IndexedPKIXParameters(Set<TrustAnchor> anchors)
-            throws KeyStoreException, InvalidAlgorithmParameterException,
-            CertificateEncodingException {
-        super(anchors);
-
-        for (TrustAnchor anchor : anchors) {
-            X509Certificate cert = anchor.getTrustedCert();
-
-            Bytes encoded = new Bytes(cert.getEncoded());
-            encodings.put(encoded, anchor);
-
-            X500Principal subject = cert.getSubjectX500Principal();
-            if (bySubject.put(subject, anchor) != null) {
-                // TODO: Should we allow this?
-                throw new KeyStoreException("Two certs have the same subject: "
-                        + subject);
-            }
-
-            X500Principal ca = anchor.getCA();
-            List<TrustAnchor> caAnchors = byCA.get(ca);
-            if (caAnchors == null) {
-                caAnchors = new ArrayList<TrustAnchor>();
-                byCA.put(ca, caAnchors);
-            }
-            caAnchors.add(anchor);
-        }
-    }
-
-    TrustAnchor findTrustAnchor(X509Certificate cert, CertPath certPath,
-            int index) throws CertPathValidatorException {
-        // Mimic the alg in CertPathValidatorUtilities.findTrustAnchor().
-        Exception verificationException = null;
-        X500Principal issuer = cert.getIssuerX500Principal();
-
-        List<TrustAnchor> anchors = byCA.get(issuer);
-        if (anchors != null) {
-            for (TrustAnchor caAnchor : anchors) {
-                try {
-                    cert.verify(caAnchor.getCAPublicKey());
-                    return caAnchor;
-                } catch (Exception e) {
-                    verificationException = e;
-                }
-            }
-        }
-
-        TrustAnchor anchor = bySubject.get(issuer);
-        if (anchor != null) {
-            try {
-                cert.verify(anchor.getTrustedCert().getPublicKey());
-                return anchor;
-            } catch (Exception e) {
-                verificationException = e;
-            }
-        }
-
-        try {
-            Bytes encoded = new Bytes(cert.getEncoded());
-            anchor = encodings.get(encoded);
-            if (anchor != null) {
-                return anchor;
-            }
-        } catch (Exception e) {
-            Logger.getLogger(IndexedPKIXParameters.class.getName()).log(
-                    Level.WARNING, "Error encoding cert.", e);
-        }
-
-        // Throw last verification exception.
-        if (verificationException != null) {
-            throw new CertPathValidatorException("TrustAnchor found but"
-                    + " certificate verification failed.",
-                    verificationException, certPath, index);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns true if the given certificate is found in the trusted key
-     * store.
-     */
-    public boolean isDirectlyTrusted(X509Certificate cert) {
-        try {
-            Bytes encoded = new Bytes(cert.getEncoded());
-            return encodings.containsKey(encoded);
-        } catch (Exception e) {
-            Logger.getLogger(IndexedPKIXParameters.class.getName()).log(
-                    Level.WARNING, "Error encoding cert.", e);
-            return false;
-        }
-    }
-
-    /**
-     * Wraps a byte[] and adds equals() and hashCode() support.
-     */
-    static class Bytes {
-        final byte[] bytes;
-        final int hash;
-        Bytes(byte[] bytes) {
-            this.bytes = bytes;
-            this.hash = Arrays.hashCode(bytes);
-        }
-        @Override public int hashCode() {
-            return hash;
-        }
-        @Override public boolean equals(Object o) {
-            return Arrays.equals(bytes, ((Bytes) o).bytes);
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java b/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
index e2b22cb..68791ab 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
@@ -1,13 +1,67 @@
 package org.bouncycastle.jce.provider;
 
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.BlowfishEngine;
+// import org.bouncycastle.crypto.engines.CAST5Engine;
+// import org.bouncycastle.crypto.engines.CAST6Engine;
+// END android-removed
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.GOST28147Engine;
+// END android-removed
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.engines.RC532Engine;
+import org.bouncycastle.crypto.engines.RC564Engine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.RC6Engine;
+// import org.bouncycastle.crypto.engines.RijndaelEngine;
+// import org.bouncycastle.crypto.engines.SEEDEngine;
+// import org.bouncycastle.crypto.engines.SerpentEngine;
+// import org.bouncycastle.crypto.engines.SkipjackEngine;
+// import org.bouncycastle.crypto.engines.TEAEngine;
+// import org.bouncycastle.crypto.engines.TwofishEngine;
+// import org.bouncycastle.crypto.engines.XTEAEngine;
+// END android-removed
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.CTSBlockCipher;
+import org.bouncycastle.crypto.modes.EAXBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
+// import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+// END android-removed
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.paddings.TBCPadding;
+import org.bouncycastle.crypto.paddings.X923Padding;
+import org.bouncycastle.crypto.paddings.ZeroBytePadding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.params.ParametersWithSBox;
+// END android-removed
+import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.crypto.params.RC5Parameters;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
+// END android-removed
+import org.bouncycastle.util.Strings;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -19,39 +73,14 @@
 import javax.crypto.spec.PBEParameterSpec;
 import javax.crypto.spec.RC2ParameterSpec;
 import javax.crypto.spec.RC5ParameterSpec;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.engines.*;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.modes.CFBBlockCipher;
-import org.bouncycastle.crypto.modes.CTSBlockCipher;
-import org.bouncycastle.crypto.modes.GOFBBlockCipher;
-import org.bouncycastle.crypto.modes.OFBBlockCipher;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
-// import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
-// END android-removed
-import org.bouncycastle.crypto.modes.SICBlockCipher;
-import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
-import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
-import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
-import org.bouncycastle.crypto.paddings.TBCPadding;
-import org.bouncycastle.crypto.paddings.X923Padding;
-import org.bouncycastle.crypto.paddings.ZeroBytePadding;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.params.ParametersWithSBox;
-import org.bouncycastle.crypto.params.RC2Parameters;
-import org.bouncycastle.crypto.params.RC5Parameters;
-// BEGIN android-removed
-// import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
-// END android-removed
-import org.bouncycastle.util.Strings;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
 
 public class JCEBlockCipher extends WrapCipherSpi
     implements PBE
@@ -69,12 +98,12 @@
                                     };
  
     private BlockCipher             baseEngine;
-    private BufferedBlockCipher     cipher;
+    private GenericBlockCipher      cipher;
     private ParametersWithIV        ivParam;
 
     private int                     ivLength = 0;
 
-    private boolean                 padded = true;
+    private boolean                 padded;
     
     private PBEParameterSpec        pbeSpec = null;
     private String                  pbeAlgorithm = null;
@@ -86,7 +115,7 @@
     {
         baseEngine = engine;
 
-        cipher = new PaddedBufferedBlockCipher(engine);
+        cipher = new BufferedGenericBlockCipher(engine);
     }
         
     protected JCEBlockCipher(
@@ -95,7 +124,17 @@
     {
         baseEngine = engine;
 
-        this.cipher = new PaddedBufferedBlockCipher(engine);
+        this.cipher = new BufferedGenericBlockCipher(engine);
+        this.ivLength = ivLength / 8;
+    }
+
+    protected JCEBlockCipher(
+        BufferedBlockCipher engine,
+        int                 ivLength)
+    {
+        baseEngine = engine.getUnderlyingCipher();
+
+        this.cipher = new BufferedGenericBlockCipher(engine);
         this.ivLength = ivLength / 8;
     }
 
@@ -170,12 +209,12 @@
         if (modeName.equals("ECB"))
         {
             ivLength = 0;
-            cipher = new PaddedBufferedBlockCipher(baseEngine);
+            cipher = new BufferedGenericBlockCipher(baseEngine);
         }
         else if (modeName.equals("CBC"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new PaddedBufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(
                             new CBCBlockCipher(baseEngine));
         }
         else if (modeName.startsWith("OFB"))
@@ -185,12 +224,12 @@
             {
                 int wordSize = Integer.parseInt(modeName.substring(3));
 
-                cipher = new PaddedBufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(
                                 new OFBBlockCipher(baseEngine, wordSize));
             }
             else
             {
-                cipher = new PaddedBufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(
                         new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
             }
         }
@@ -201,35 +240,28 @@
             {
                 int wordSize = Integer.parseInt(modeName.substring(3));
 
-                cipher = new PaddedBufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(
                                 new CFBBlockCipher(baseEngine, wordSize));
             }
             else
             {
-                cipher = new PaddedBufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(
                         new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
             }
         }
         // BEGIN android-removed
         // else if (modeName.startsWith("PGP"))
         // {
-        //     if (modeName.equalsIgnoreCase("PGPCFBwithIV"))
-        //     {
-        //         ivLength = baseEngine.getBlockSize();
-        //         cipher = new PaddedBufferedBlockCipher(
-        //             new PGPCFBBlockCipher(baseEngine, true));
-        //     }
-        //     else
-        //     {
-        //         ivLength = baseEngine.getBlockSize();
-        //         cipher = new PaddedBufferedBlockCipher(
-        //             new PGPCFBBlockCipher(baseEngine, false));
-        //     }
+        //     boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
+        //
+        //     ivLength = baseEngine.getBlockSize();
+        //     cipher = new BufferedGenericBlockCipher(
+        //         new PGPCFBBlockCipher(baseEngine, inlineIV));
         // }
         // else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
         // {
         //     ivLength = 0;
-        //     cipher = new PaddedBufferedBlockCipher(
+        //     cipher = new BufferedGenericBlockCipher(
         //         new OpenPGPCFBBlockCipher(baseEngine));
         // }
         // END android-removed
@@ -240,25 +272,40 @@
             {
                 throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
             }
-            cipher = new BufferedBlockCipher(
-                        new SICBlockCipher(baseEngine));
+            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                        new SICBlockCipher(baseEngine)));
         }
         else if (modeName.startsWith("CTR"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedBlockCipher(
-                        new SICBlockCipher(baseEngine));
+            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                        new SICBlockCipher(baseEngine)));
         }
         else if (modeName.startsWith("GOFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedBlockCipher(
-                        new GOFBBlockCipher(baseEngine));
+            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                        new GOFBBlockCipher(baseEngine)));
         }
         else if (modeName.startsWith("CTS"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new CTSBlockCipher(new CBCBlockCipher(baseEngine));
+            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+        }
+        else if (modeName.startsWith("CCM"))
+        {
+            ivLength = baseEngine.getBlockSize();
+            cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+        }
+        else if (modeName.startsWith("EAX"))
+        {
+            ivLength = baseEngine.getBlockSize();
+            cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine));
+        }
+        else if (modeName.startsWith("GCM"))
+        {
+            ivLength = baseEngine.getBlockSize();
+            cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
         }
         else
         {
@@ -274,45 +321,51 @@
 
         if (paddingName.equals("NOPADDING"))
         {
-            padded = false;
-            
-            if (!(cipher instanceof CTSBlockCipher))
+            if (cipher.wrapOnNoPadding())
             {
-                cipher = new BufferedBlockCipher(cipher.getUnderlyingCipher());
+                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
             }
         }
-        else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher());
-        }
-        else if (paddingName.equals("ZEROBYTEPADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding());
-        }
-        else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding());
-        }
-        else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher(), new X923Padding());
-        }
-        else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding());
-        }
-        else if (paddingName.equals("TBCPADDING"))
-        {
-            cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding());
-        }
         else if (paddingName.equals("WITHCTS"))
         {
-            padded = false;
-            cipher = new CTSBlockCipher(cipher.getUnderlyingCipher());
+            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher()));
         }
         else
         {
-            throw new NoSuchPaddingException("Padding " + padding + " unknown.");
+            padded = true;
+
+            if (isAEADModeName(modeName))
+            {
+                throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes.");
+            }
+            else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher());
+            }
+            else if (paddingName.equals("ZEROBYTEPADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding());
+            }
+            else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding());
+            }
+            else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding());
+            }
+            else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding());
+            }
+            else if (paddingName.equals("TBCPADDING"))
+            {
+                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding());
+            }
+            else
+            {
+                throw new NoSuchPaddingException("Padding " + padding + " unknown.");
+            }
         }
     }
 
@@ -391,7 +444,7 @@
             {
                 IvParameterSpec p = (IvParameterSpec)params;
 
-                if (p.getIV().length != ivLength)
+                if (p.getIV().length != ivLength && !isAEADModeName(modeName))
                 {
                     throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long.");
                 }
@@ -543,7 +596,7 @@
                 }
                 catch (Exception e)
                 {
-                    continue;
+                    // try again if possible
                 }
             }
 
@@ -653,6 +706,11 @@
             throw new BadPaddingException(e.getMessage());
         }
 
+        if (len == tmp.length)
+        {
+            return tmp;
+        }
+
         byte[]  out = new byte[len];
 
         System.arraycopy(tmp, 0, out, 0, len);
@@ -700,6 +758,12 @@
         }
     }
 
+    private boolean isAEADModeName(
+        String modeName)
+    {
+        return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName);
+    }
+
     /*
      * The ciphers that inherit from us.
      */
@@ -716,17 +780,19 @@
         }
     }
 
-    /**
-     * DESCBC
-     */
-    static public class DESCBC
-        extends JCEBlockCipher
-    {
-        public DESCBC()
-        {
-            super(new CBCBlockCipher(new DESEngine()), 64);
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * DESCBC
+    //  */
+    // static public class DESCBC
+    //     extends JCEBlockCipher
+    // {
+    //     public DESCBC()
+    //     {
+    //         super(new CBCBlockCipher(new DESEngine()), 64);
+    //     }
+    // }
+    // END android-removed
 
     /**
      * DESede
@@ -740,22 +806,22 @@
         }
     }
 
-    /**
-     * DESedeCBC
-     */
-    static public class DESedeCBC
-        extends JCEBlockCipher
-    {
-        public DESedeCBC()
-        {
-            super(new CBCBlockCipher(new DESedeEngine()), 64);
-        }
-    }
-
-    /**
-     *  GOST28147
-     */
     // BEGIN android-removed
+    // /**
+    //  * DESedeCBC
+    //  */
+    // static public class DESedeCBC
+    //     extends JCEBlockCipher
+    // {
+    //     public DESedeCBC()
+    //     {
+    //         super(new CBCBlockCipher(new DESedeEngine()), 64);
+    //     }
+    // }
+    //
+    // /**
+    //  *  GOST28147
+    //  */
     // static public class GOST28147
     //     extends JCEBlockCipher
     // {
@@ -764,7 +830,7 @@
     //         super(new GOST28147Engine());
     //     }
     // }
-    //
+    //    
     // static public class GOST28147cbc
     //     extends JCEBlockCipher
     // {
@@ -773,74 +839,192 @@
     //         super(new CBCBlockCipher(new GOST28147Engine()), 64);
     //     }
     // }
-    // END android-removed
-
-    /**
-     * AES
-     */
-    static public class AES
-        extends JCEBlockCipher
-    {
-        public AES()
-        {
-            super(new AESFastEngine());
-        }
-    }
-
-    /**
-     * AESCBC
-     */
-    static public class AESCBC
-        extends JCEBlockCipher
-    {
-        public AESCBC()
-        {
-            super(new CBCBlockCipher(new AESFastEngine()), 128);
-        }
-    }
-
-    /**
-     * AESCFB
-     */
-    static public class AESCFB
-        extends JCEBlockCipher
-    {
-        public AESCFB()
-        {
-            super(new CFBBlockCipher(new AESFastEngine(), 128), 128);
-        }
-    }
-    
-    /**
-     * AESOFB
-     */
-    static public class AESOFB
-        extends JCEBlockCipher
-    {
-        public AESOFB()
-        {
-            super(new OFBBlockCipher(new AESFastEngine(), 128), 128);
-        }
-    }
-
-    /**
-     * Camellia
-     */
-    // BEGIN android-removed
-    // static public class Camellia
+    //
+    // /**
+    //  * SKIPJACK
+    //  */
+    // static public class Skipjack
     //     extends JCEBlockCipher
     // {
-    //     public Camellia()
+    //     public Skipjack()
     //     {
-    //         super(new CamelliaEngine());
+    //         super(new SkipjackEngine());
     //     }
     // }
-    // END android-removed
-    
-    /**
-     * CAST5
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * Blowfish
+    //  */
+    // static public class Blowfish
+    //     extends JCEBlockCipher
+    // {
+    //     public Blowfish()
+    //     {
+    //         super(new BlowfishEngine());
+    //     }
+    // }
+    //
+    // /**
+    //  * Blowfish CBC
+    //  */
+    // static public class BlowfishCBC
+    //     extends JCEBlockCipher
+    // {
+    //     public BlowfishCBC()
+    //     {
+    //         super(new CBCBlockCipher(new BlowfishEngine()), 64);
+    //     }
+    // }
+    //
+    // /**
+    //  * Twofish
+    //  */
+    // static public class Twofish
+    //     extends JCEBlockCipher
+    // {
+    //     public Twofish()
+    //     {
+    //         super(new TwofishEngine());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC2
+    //  */
+    // static public class RC2
+    //     extends JCEBlockCipher
+    // {
+    //     public RC2()
+    //     {
+    //         super(new RC2Engine());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC2CBC
+    //  */
+    // static public class RC2CBC
+    //     extends JCEBlockCipher
+    // {
+    //     public RC2CBC()
+    //     {
+    //         super(new CBCBlockCipher(new RC2Engine()), 64);
+    //     }
+    // }
+    //
+    // /**
+    //  * RC5
+    //  */
+    // static public class RC5
+    //     extends JCEBlockCipher
+    // {
+    //     public RC5()
+    //     {
+    //         super(new RC532Engine());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC564
+    //  */
+    // static public class RC564
+    //     extends JCEBlockCipher
+    // {
+    //     public RC564()
+    //     {
+    //         super(new RC564Engine());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC6
+    //  */
+    // static public class RC6
+    //     extends JCEBlockCipher
+    // {
+    //     public RC6()
+    //     {
+    //         super(new RC6Engine());
+    //     }
+    // }
+    //
+    // /**
+    //  * AES
+    //  */
+    // static public class AES
+    //     extends JCEBlockCipher
+    // {
+    //     public AES()
+    //     {
+    //         super(new AESFastEngine());
+    //     }
+    // }
+    //
+    // /**
+    //  * AESCBC
+    //  */
+    // static public class AESCBC
+    //     extends JCEBlockCipher
+    // {
+    //     public AESCBC()
+    //     {
+    //         super(new CBCBlockCipher(new AESFastEngine()), 128);
+    //     }
+    // }
+    //
+    // /**
+    //  * AESCFB
+    //  */
+    // static public class AESCFB
+    //     extends JCEBlockCipher
+    // {
+    //     public AESCFB()
+    //     {
+    //         super(new CFBBlockCipher(new AESFastEngine(), 128), 128);
+    //     }
+    // }
+    //
+    // /**
+    //  * AESOFB
+    //  */
+    // static public class AESOFB
+    //     extends JCEBlockCipher
+    // {
+    //     public AESOFB()
+    //     {
+    //         super(new OFBBlockCipher(new AESFastEngine(), 128), 128);
+    //     }
+    // }
+    //
+    // /**
+    //  * Rijndael
+    //  */
+    // static public class Rijndael
+    //     extends JCEBlockCipher
+    // {
+    //     public Rijndael()
+    //     {
+    //         super(new RijndaelEngine());
+    //     }
+    // }
+    //
+    // /**
+    //  * Serpent
+    //  */
+    // static public class Serpent
+    //     extends JCEBlockCipher
+    // {
+    //     public Serpent()
+    //     {
+    //         super(new SerpentEngine());
+    //     }
+    // }
+    //
+    //
+    //
+    // /**
+    //  * CAST5
+    //  */
     // static public class CAST5
     //     extends JCEBlockCipher
     // {
@@ -849,12 +1033,10 @@
     //         super(new CAST5Engine());
     //     }
     // }
-    // END android-removed
-
-    /**
-     * CAST5 CBC
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * CAST5 CBC
+    //  */
     // static public class CAST5CBC
     //     extends JCEBlockCipher
     // {
@@ -863,12 +1045,10 @@
     //         super(new CBCBlockCipher(new CAST5Engine()), 64);
     //     }
     // }
-    // END android-removed
-
-    /**
-     * CAST6
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * CAST6
+    //  */
     // static public class CAST6
     //     extends JCEBlockCipher
     // {
@@ -877,7 +1057,43 @@
     //         super(new CAST6Engine());
     //     }
     // }
-    // BEGIN android-removed
+    // 
+    // /**
+    //  * TEA
+    //  */
+    // static public class TEA
+    //     extends JCEBlockCipher
+    // {
+    //     public TEA()
+    //     {
+    //         super(new TEAEngine());
+    //     }
+    // }
+    //
+    // /**
+    //  * XTEA
+    //  */
+    // static public class XTEA
+    //     extends JCEBlockCipher
+    // {
+    //     public XTEA()
+    //     {
+    //         super(new XTEAEngine());
+    //     }
+    // }
+    // 
+    // /**
+    //  * SEED
+    //  */
+    // static public class SEED
+    //     extends JCEBlockCipher
+    // {
+    //     public SEED()
+    //     {
+    //         super(new SEEDEngine());
+    //     }
+    // }
+    // END android-removed
 
     /**
      * PBEWithMD5AndDES
@@ -891,29 +1107,31 @@
         }
     }
 
-    /**
-     * PBEWithMD5AndRC2
-     */
-    static public class PBEWithMD5AndRC2
-        extends JCEBlockCipher
-    {
-        public PBEWithMD5AndRC2()
-        {
-            super(new CBCBlockCipher(new RC2Engine()));
-        }
-    }
-
-    /**
-     * PBEWithSHA1AndDES
-     */
-    static public class PBEWithSHA1AndDES
-        extends JCEBlockCipher
-    {
-        public PBEWithSHA1AndDES()
-        {
-            super(new CBCBlockCipher(new DESEngine()));
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * PBEWithMD5AndRC2
+    //  */
+    // static public class PBEWithMD5AndRC2
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithMD5AndRC2()
+    //     {
+    //         super(new CBCBlockCipher(new RC2Engine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * PBEWithSHA1AndDES
+    //  */
+    // static public class PBEWithSHA1AndDES
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithSHA1AndDES()
+    //     {
+    //         super(new CBCBlockCipher(new DESEngine()));
+    //     }
+    // }
+    // END android-removed
 
     /**
      * PBEWithSHAAnd3-KeyTripleDES-CBC
@@ -927,17 +1145,19 @@
         }
     }
 
-    /**
-     * PBEWithSHAAnd2-KeyTripleDES-CBC
-     */
-    static public class PBEWithSHAAndDES2Key
-        extends JCEBlockCipher
-    {
-        public PBEWithSHAAndDES2Key()
-        {
-            super(new CBCBlockCipher(new DESedeEngine()));
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * PBEWithSHAAnd2-KeyTripleDES-CBC
+    //  */
+    // static public class PBEWithSHAAndDES2Key
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithSHAAndDES2Key()
+    //     {
+    //         super(new CBCBlockCipher(new DESedeEngine()));
+    //     }
+    // }
+    // END android-removed
     
     /**
      * PBEWithAES-CBC
@@ -951,15 +1171,190 @@
         }
     }
     
-    /**
-     * PBEWITHSHAAND40BITRC2-CBC
-     */
-    static public class PBEWithSHAAnd40BitRC2
-        extends JCEBlockCipher
+    // BEGIN android-removed
+    // /**
+    //  * PBEWITHSHAAND40BITRC2-CBC
+    //  */
+    // static public class PBEWithSHAAnd40BitRC2
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithSHAAnd40BitRC2()
+    //     {
+    //         super(new CBCBlockCipher(new RC2Engine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * PBEWithSHAAndTwofish-CBC
+    //  */
+    // static public class PBEWithSHAAndTwofish
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithSHAAndTwofish()
+    //     {
+    //         super(new CBCBlockCipher(new TwofishEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * PBEWithAES-CBC
+    //  */
+    // static public class PBEWithAESCBC
+    //     extends JCEBlockCipher
+    // {
+    //     public PBEWithAESCBC()
+    //     {
+    //         super(new CBCBlockCipher(new AESFastEngine()));
+    //     }
+    // }
+    // END android-removed
+
+    static private interface GenericBlockCipher
     {
-        public PBEWithSHAAnd40BitRC2()
+        public void init(boolean forEncryption, CipherParameters params)
+            throws IllegalArgumentException;
+
+        public boolean wrapOnNoPadding();
+
+        public String getAlgorithmName();
+
+        public BlockCipher getUnderlyingCipher();
+
+        public int getOutputSize(int len);
+
+        public int getUpdateOutputSize(int len);
+
+        public int processByte(byte in, byte[] out, int outOff)
+            throws DataLengthException;
+
+        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+            throws DataLengthException;
+
+        public int doFinal(byte[] out, int outOff)
+            throws IllegalStateException, InvalidCipherTextException;
+    }
+
+    private static class BufferedGenericBlockCipher
+        implements GenericBlockCipher
+    {
+        private BufferedBlockCipher cipher;
+
+        BufferedGenericBlockCipher(BufferedBlockCipher cipher)
         {
-            super(new CBCBlockCipher(new RC2Engine()));
+            this.cipher = cipher;
+        }
+
+        BufferedGenericBlockCipher(BlockCipher cipher)
+        {
+            this.cipher = new PaddedBufferedBlockCipher(cipher);
+        }
+
+        BufferedGenericBlockCipher(BlockCipher cipher, BlockCipherPadding padding)
+        {
+            this.cipher = new PaddedBufferedBlockCipher(cipher, padding);
+        }
+
+        public void init(boolean forEncryption, CipherParameters params)
+            throws IllegalArgumentException
+        {
+            cipher.init(forEncryption, params);
+        }
+
+        public boolean wrapOnNoPadding()
+        {
+            return !(cipher instanceof CTSBlockCipher);
+        }
+
+        public String getAlgorithmName()
+        {
+            return cipher.getUnderlyingCipher().getAlgorithmName();
+        }
+
+        public BlockCipher getUnderlyingCipher()
+        {
+            return cipher.getUnderlyingCipher();
+        }
+
+        public int getOutputSize(int len)
+        {
+            return cipher.getOutputSize(len);
+        }
+
+        public int getUpdateOutputSize(int len)
+        {
+            return cipher.getUpdateOutputSize(len);
+        }
+
+        public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+        {
+            return cipher.processByte(in, out, outOff);
+        }
+
+        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+        {
+            return cipher.processBytes(in, inOff, len, out, outOff);
+        }
+
+        public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+        {
+            return cipher.doFinal(out, outOff);
+        }
+    }
+
+    private static class AEADGenericBlockCipher
+        implements GenericBlockCipher
+    {
+        private AEADBlockCipher cipher;
+
+        AEADGenericBlockCipher(AEADBlockCipher cipher)
+        {
+            this.cipher = cipher;
+        }
+
+        public void init(boolean forEncryption, CipherParameters params)
+            throws IllegalArgumentException
+        {
+            cipher.init(forEncryption, params);
+        }
+
+        public String getAlgorithmName()
+        {
+            return cipher.getUnderlyingCipher().getAlgorithmName();
+        }
+
+        public boolean wrapOnNoPadding()
+        {
+            return false;
+        }
+
+        public BlockCipher getUnderlyingCipher()
+        {
+            return cipher.getUnderlyingCipher();
+        }
+
+        public int getOutputSize(int len)
+        {
+            return cipher.getOutputSize(len);
+        }
+
+        public int getUpdateOutputSize(int len)
+        {
+            return cipher.getUpdateOutputSize(len);
+        }
+
+        public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+        {
+            return cipher.processByte(in, out, outOff);
+        }
+
+        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+        {
+            return cipher.processBytes(in, inOff, len, out, outOff);
+        }
+
+        public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+        {
+            return cipher.doFinal(out, outOff);
         }
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java b/src/main/java/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java
index f215029..75fbdf7 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEDHKeyAgreement.java
@@ -1,11 +1,7 @@
 package org.bouncycastle.jce.provider;
 
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
+import org.bouncycastle.crypto.params.DESParameters;
+import org.bouncycastle.util.Strings;
 
 import javax.crypto.KeyAgreementSpi;
 import javax.crypto.SecretKey;
@@ -14,6 +10,13 @@
 import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Hashtable;
 
 /**
  * Diffie-Hellman key agreement. There's actually a better way of doing this
@@ -29,7 +32,22 @@
     private BigInteger      result;
 
     private SecureRandom    random;
-    
+
+    private static final Hashtable algorithms = new Hashtable();
+
+    static
+    {
+        // BEGIN android-changed
+        Integer i64 = Integer.valueOf(64);
+        Integer i192 = Integer.valueOf(192);
+        Integer i128 = Integer.valueOf(128);
+        // END android-changed
+
+        algorithms.put("DES", i64);
+        algorithms.put("DESEDE", i192);
+        algorithms.put("BLOWFISH", i128);
+    }
+
     private byte[] bigIntToBytes(
         BigInteger    r)
     {
@@ -120,8 +138,26 @@
         {
             throw new IllegalStateException("Diffie-Hellman not initialised.");
         }
-        
-        return new SecretKeySpec(bigIntToBytes(result), algorithm);
+
+        String algKey = Strings.toUpperCase(algorithm);
+        byte[] res = bigIntToBytes(result);
+
+        if (algorithms.containsKey(algKey))
+        {
+            Integer length = (Integer)algorithms.get(algKey);
+
+            byte[] key = new byte[length.intValue() / 8];
+            System.arraycopy(res, 0, key, 0, key.length);
+
+            if (algKey.startsWith("DES"))
+            {
+                DESParameters.setOddParity(key);
+            }
+            
+            return new SecretKeySpec(key, algorithm);
+        }
+
+        return new SecretKeySpec(res, algorithm);
     }
 
     protected void engineInit(
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
index 07c71bf..3da31fb 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -1,23 +1,9 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.math.BigInteger;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.DHPrivateKeySpec;
-
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
@@ -25,15 +11,25 @@
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
 public class JCEDHPrivateKey
     implements DHPrivateKey, PKCS12BagAttributeCarrier
 {
+    static final long serialVersionUID = 311058815616901812L;
+    
     BigInteger      x;
 
     DHParameterSpec dhSpec;
 
-    private Hashtable   pkcs12Attributes = new Hashtable();
-    private Vector      pkcs12Ordering = new Vector();
+    private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
     protected JCEDHPrivateKey()
     {
@@ -74,7 +70,7 @@
         DHPrivateKeyParameters  params)
     {
         this.x = params.getX();
-        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG());
+        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
     }
 
     public String getAlgorithm()
@@ -138,18 +134,17 @@
         DERObjectIdentifier oid,
         DEREncodable        attribute)
     {
-        pkcs12Attributes.put(oid, attribute);
-        pkcs12Ordering.addElement(oid);
+        attrCarrier.setBagAttribute(oid, attribute);
     }
 
     public DEREncodable getBagAttribute(
         DERObjectIdentifier oid)
     {
-        return (DEREncodable)pkcs12Attributes.get(oid);
+        return attrCarrier.getBagAttribute(oid);
     }
 
     public Enumeration getBagAttributeKeys()
     {
-        return pkcs12Ordering.elements();
+        return attrCarrier.getBagAttributeKeys();
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
index 0534b4d..e343af3 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
@@ -1,27 +1,26 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.math.BigInteger;
-
-import javax.crypto.interfaces.DHPublicKey;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.DHPublicKeySpec;
-
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+
 public class JCEDHPublicKey
     implements DHPublicKey
 {
+    static final long serialVersionUID = -216691575254424324L;
+    
     private BigInteger              y;
     private DHParameterSpec         dhSpec;
 
@@ -43,7 +42,7 @@
         DHPublicKeyParameters  params)
     {
         this.y = params.getY();
-        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), 0);
+        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
     }
 
     JCEDHPublicKey(
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEDigestUtil.java b/src/main/java/org/bouncycastle/jce/provider/JCEDigestUtil.java
index a9f6d6a..8f30b25 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEDigestUtil.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEDigestUtil.java
@@ -12,7 +12,9 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.MD5Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.digests.SHA224Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// END android-removed
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
@@ -22,7 +24,9 @@
 {
     private static Set md5 = new HashSet();
     private static Set sha1 = new HashSet();
-    private static Set sha224 = new HashSet();
+    // BEGIN android-removed
+    // private static Set sha224 = new HashSet();
+    // END android-removed
     private static Set sha256 = new HashSet();
     private static Set sha384 = new HashSet();
     private static Set sha512 = new HashSet();
@@ -38,9 +42,11 @@
         sha1.add("SHA-1");
         sha1.add(OIWObjectIdentifiers.idSHA1.getId());
         
-        sha224.add("SHA224");
-        sha224.add("SHA-224");
-        sha224.add(NISTObjectIdentifiers.id_sha224.getId());
+        // BEGIN android-removed
+        // sha224.add("SHA224");
+        // sha224.add("SHA-224");
+        // sha224.add(NISTObjectIdentifiers.id_sha224.getId());
+        // END android-removed
         
         sha256.add("SHA256");
         sha256.add("SHA-256");
@@ -61,9 +67,11 @@
         oids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
         oids.put(OIWObjectIdentifiers.idSHA1.getId(), OIWObjectIdentifiers.idSHA1);
         
-        oids.put("SHA224", NISTObjectIdentifiers.id_sha224);
-        oids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
-        oids.put(NISTObjectIdentifiers.id_sha224.getId(), NISTObjectIdentifiers.id_sha224);
+        // BEGIN android-removed
+        // oids.put("SHA224", NISTObjectIdentifiers.id_sha224);
+        // oids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
+        // oids.put(NISTObjectIdentifiers.id_sha224.getId(), NISTObjectIdentifiers.id_sha224);
+        // END android-removed
         
         oids.put("SHA256", NISTObjectIdentifiers.id_sha256);
         oids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
@@ -91,10 +99,12 @@
         {
             return new MD5Digest();
         }
-        if (sha224.contains(digestName))
-        {
-            return new SHA224Digest();
-        }
+        // BEGIN android-removed
+        // if (sha224.contains(digestName))
+        // {
+        //     return new SHA224Digest();
+        // }
+        // END android-removed
         if (sha256.contains(digestName))
         {
             return new SHA256Digest();
@@ -116,7 +126,9 @@
         String digest2)
     {
         return (sha1.contains(digest1) && sha1.contains(digest2))
-            || (sha224.contains(digest1) && sha224.contains(digest2))
+            // BEGIN android-removed
+            // || (sha224.contains(digest1) && sha224.contains(digest2))
+            // END android-removed
             || (sha256.contains(digest1) && sha256.contains(digest2))
             || (sha384.contains(digest1) && sha384.contains(digest2))
             || (sha512.contains(digest1) && sha512.contains(digest2))
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEIESCipher.java b/src/main/java/org/bouncycastle/jce/provider/JCEIESCipher.java
deleted file mode 100644
index 95ee2b6..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/JCEIESCipher.java
+++ /dev/null
@@ -1,408 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.io.ByteArrayOutputStream;
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.interfaces.DHPrivateKey;
-
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.agreement.DHBasicAgreement;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
-// END android-removed
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.engines.IESEngine;
-import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
-import org.bouncycastle.crypto.macs.HMac;
-import org.bouncycastle.crypto.params.IESParameters;
-// BEGIN android-removed
-// import org.bouncycastle.jce.interfaces.ECPrivateKey;
-// import org.bouncycastle.jce.interfaces.ECPublicKey;
-// END android-removed
-import org.bouncycastle.jce.interfaces.IESKey;
-import org.bouncycastle.jce.spec.IESParameterSpec;
-
-public class JCEIESCipher extends WrapCipherSpi
-{
-    private IESEngine               cipher;
-    private int                     state = -1;
-    private ByteArrayOutputStream   buffer = new ByteArrayOutputStream();
-    private AlgorithmParameters     engineParam = null;
-    private IESParameterSpec        engineParams = null;
-
-    //
-    // specs we can handle.
-    //
-    private Class[]                 availableSpecs =
-                                    {
-                                        IESParameterSpec.class
-                                    };
-
-    public JCEIESCipher(
-        IESEngine   engine)
-    {
-        cipher = engine;
-    }
-
-    protected int engineGetBlockSize() 
-    {
-        return 0;
-    }
-
-    protected byte[] engineGetIV() 
-    {
-        return null;
-    }
-
-    protected int engineGetKeySize(
-        Key     key) 
-    {
-        IESKey   ieKey = (IESKey)key;
-
-        if (ieKey.getPrivate() instanceof DHPrivateKey)
-        {
-            DHPrivateKey   k = (DHPrivateKey)ieKey.getPrivate();
-
-            return k.getX().bitLength();
-        }
-        // BEGIN android-removed
-        // else if (ieKey.getPrivate() instanceof ECPrivateKey)
-        // {
-        //     ECPrivateKey   k = (ECPrivateKey)ieKey.getPrivate();
-        //
-        //     return k.getD().bitLength();
-        // }
-        // END android-removed
-
-        throw new IllegalArgumentException("not an IE key!");
-    }
-
-    protected int engineGetOutputSize(
-        int     inputLen) 
-    {
-        if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
-        {
-            return buffer.size() + inputLen + 20; /* SHA1 MAC size */
-        }
-        else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
-        {
-            return buffer.size() + inputLen - 20;
-        }
-        else
-        {
-            throw new IllegalStateException("cipher not initialised");
-        }
-    }
-
-    protected AlgorithmParameters engineGetParameters() 
-    {
-        if (engineParam == null)
-        {
-            if (engineParams != null)
-            {
-                String  name = "IES";
-
-                try
-                {
-                    engineParam = AlgorithmParameters.getInstance(name, "BC");
-                    engineParam.init(engineParams);
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException(e.toString());
-                }
-            }
-        }
-
-        return engineParam;
-    }
-
-    protected void engineSetMode(
-        String  mode) 
-    {
-        throw new IllegalArgumentException("can't support mode " + mode);
-    }
-
-    protected void engineSetPadding(
-        String  padding) 
-        throws NoSuchPaddingException
-    {
-        throw new NoSuchPaddingException(padding + " unavailable with RSA.");
-    }
-
-    protected void engineInit(
-        int                     opmode,
-        Key                     key,
-        AlgorithmParameterSpec  params,
-        SecureRandom            random) 
-    throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        if (!(key instanceof IESKey))
-        {
-            throw new InvalidKeyException("must be passed IE key");
-        }
-
-        if (params == null && (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE))
-        {
-            //
-            // if nothing is specified we set up for a 128 bit mac, with
-            // 128 bit derivation vectors.
-            //
-            byte[]  d = new byte[16];
-            byte[]  e = new byte[16];
-
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            random.nextBytes(d);
-            random.nextBytes(e);
-
-            params = new IESParameterSpec(d, e, 128);
-        }
-        else if (!(params instanceof IESParameterSpec))
-        {
-            throw new InvalidAlgorithmParameterException("must be passed IES parameters");
-        }
-
-        IESKey       ieKey = (IESKey)key;
-
-        CipherParameters pubKey;
-        CipherParameters privKey;
-
-        // BEGIN android-removed
-        // if (ieKey.getPublic() instanceof ECPublicKey)
-        // {
-        //     pubKey = ECUtil.generatePublicKeyParameter(ieKey.getPublic());
-        //     privKey = ECUtil.generatePrivateKeyParameter(ieKey.getPrivate());
-        // }
-        // else
-        // {
-        // END android-removed
-            pubKey = DHUtil.generatePublicKeyParameter(ieKey.getPublic());
-            privKey = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate());
-        // BEGIN android-removed
-        // }
-        // END android-removed
-
-        this.engineParams = (IESParameterSpec)params;
-
-        IESParameters       p = new IESParameters(engineParams.getDerivationV(), engineParams.getEncodingV(), engineParams.getMacKeySize());
-
-        this.state = opmode;
-
-        buffer.reset();
-
-        switch (opmode)
-        {
-        case Cipher.ENCRYPT_MODE:
-        case Cipher.WRAP_MODE:
-            cipher.init(true, privKey, pubKey, p);
-            break;
-        case Cipher.DECRYPT_MODE:
-        case Cipher.UNWRAP_MODE:
-            cipher.init(false, privKey, pubKey, p);
-            break;
-        default:
-            System.out.println("eeek!");
-        }
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        AlgorithmParameters params,
-        SecureRandom        random) 
-    throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        AlgorithmParameterSpec  paramSpec = null;
-
-        if (params != null)
-        {
-            for (int i = 0; i != availableSpecs.length; i++)
-            {
-                try
-                {
-                    paramSpec = params.getParameterSpec(availableSpecs[i]);
-                    break;
-                }
-                catch (Exception e)
-                {
-                    continue;
-                }
-            }
-
-            if (paramSpec == null)
-            {
-                throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
-            }
-        }
-
-        engineParam = params;
-        engineInit(opmode, key, paramSpec, random);
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        SecureRandom        random) 
-    throws InvalidKeyException
-    {
-        if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)
-        {
-            try
-            {
-                engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
-                return;
-            }
-            catch (InvalidAlgorithmParameterException e)
-            {
-                // fall through...
-            }
-        }
-
-        throw new IllegalArgumentException("can't handle null parameter spec in IES");
-    }
-
-    protected byte[] engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-    {
-        buffer.write(input, inputOffset, inputLen);
-        return null;
-    }
-
-    protected int engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-    {
-        buffer.write(input, inputOffset, inputLen);
-        return 0;
-    }
-
-    protected byte[] engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-        throws IllegalBlockSizeException, BadPaddingException
-    {
-        if (inputLen != 0)
-        {
-            buffer.write(input, inputOffset, inputLen);
-        }
-
-        try
-        {
-            byte[]  buf = buffer.toByteArray();
-
-            buffer.reset();
-
-            return cipher.processBlock(buf, 0, buf.length);
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadPaddingException(e.getMessage());
-        }
-    }
-
-    protected int engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-        throws IllegalBlockSizeException, BadPaddingException
-    {
-        if (inputLen != 0)
-        {
-            buffer.write(input, inputOffset, inputLen);
-        }
-
-        try
-        {
-            byte[]  buf = buffer.toByteArray();
-
-            buffer.reset();
-
-            buf = cipher.processBlock(buf, 0, buf.length);
-
-            System.arraycopy(buf, 0, output, outputOffset, buf.length);
-
-            return buf.length;
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadPaddingException(e.getMessage());
-        }
-    }
-
-    /**
-     * classes that inherit from us.
-     */
-// BEGIN android-removed
-//    static public class BrokenECIES
-//        extends JCEIESCipher
-//    {
-//        public BrokenECIES()
-//        {
-//            super(new IESEngine(
-//                   new ECDHBasicAgreement(),
-//                   new BrokenKDF2BytesGenerator(new SHA1Digest()),
-//                   new HMac(new SHA1Digest())));
-//        }
-//    }
-// END android-removed
-
-    static public class BrokenIES
-        extends JCEIESCipher
-    {
-        public BrokenIES()
-        {
-            super(new IESEngine(
-                   new DHBasicAgreement(),
-                   new BrokenKDF2BytesGenerator(new SHA1Digest()),
-                   new HMac(new SHA1Digest())));
-        }
-    }
-    
-// BEGIN android-removed
-//    static public class ECIES
-//        extends JCEIESCipher
-//    {
-//        public ECIES()
-//        {
-//            super(new IESEngine(
-//                   new ECDHBasicAgreement(),
-//                   new KDF2BytesGenerator(new SHA1Digest()),
-//                   new HMac(new SHA1Digest())));
-//        }
-//    }
-// END android-removed
-    
-    static public class IES
-        extends JCEIESCipher
-    {
-        public IES()
-        {
-            super(new IESEngine(
-                   new DHBasicAgreement(),
-                   new KDF2BytesGenerator(new SHA1Digest()),
-                   new HMac(new SHA1Digest())));
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEKeyGenerator.java b/src/main/java/org/bouncycastle/jce/provider/JCEKeyGenerator.java
index 23c3bf7..9cc328d 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEKeyGenerator.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEKeyGenerator.java
@@ -1,19 +1,18 @@
 package org.bouncycastle.jce.provider;
 
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidParameterException;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.KeyGeneratorSpi;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.KeyGenerationParameters;
 import org.bouncycastle.crypto.generators.DESKeyGenerator;
 import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
 
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
 public class JCEKeyGenerator
     extends KeyGeneratorSpi
 {
@@ -47,9 +46,8 @@
     {
         if (random != null)
         {
-            uninitialised = false;
-    
             engine.init(new KeyGenerationParameters(random, defaultKeySize));
+            uninitialised = false;
         }
     }
 
@@ -57,11 +55,10 @@
         int             keySize,
         SecureRandom    random)
     {
-        uninitialised = false;
-
         try
         {
             engine.init(new KeyGenerationParameters(random, keySize));
+            uninitialised = false;
         }
         catch (IllegalArgumentException e)
         {
@@ -73,11 +70,11 @@
     {
         if (uninitialised)
         {
-            engine.init(new KeyGenerationParameters(
-                                    new SecureRandom(), defaultKeySize));
+            engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize));
+            uninitialised = false;
         }
 
-        return (SecretKey)(new SecretKeySpec(engine.generateKey(), algName));
+        return new SecretKeySpec(engine.generateKey(), algName);
     }
 
     /**
@@ -125,8 +122,8 @@
         {
             if (uninitialised)
             {
-                engine.init(new KeyGenerationParameters(
-                                        new SecureRandom(), defaultKeySize));
+                engine.init(new KeyGenerationParameters(new SecureRandom(), defaultKeySize));
+                uninitialised = false;
             }
 
             //
@@ -148,257 +145,249 @@
         }
     }
     
-    /**
-     * generate a desEDE key in the a-b-c format.
-     */
-    public static class DESede3
-        extends JCEKeyGenerator
-    {
-        private boolean     keySizeSet = false;
-
-        public DESede3()
-        {
-            super("DESede3", 192, new DESedeKeyGenerator());
-        }
-
-        protected void engineInit(
-            int             keySize,
-            SecureRandom    random)
-        {
-            super.engineInit(keySize, random);
-            keySizeSet = true;
-        }
-
-        protected SecretKey engineGenerateKey()
-        {
-            if (uninitialised)
-            {
-                engine.init(new KeyGenerationParameters(
-                                        new SecureRandom(), defaultKeySize));
-            }
-
-            return (SecretKey)(new SecretKeySpec(engine.generateKey(), algName));
-        }
-    }
-
-    /**
-     * SKIPJACK
-     */
-    public static class Skipjack
-        extends JCEKeyGenerator
-    {
-        public Skipjack()
-        {
-            super("SKIPJACK", 80, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * Blowfish
-     */
-    public static class Blowfish
-        extends JCEKeyGenerator
-    {
-        public Blowfish()
-        {
-            super("Blowfish", 448, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * Twofish
-     */
-    public static class Twofish
-        extends JCEKeyGenerator
-    {
-        public Twofish()
-        {
-            super("Twofish", 256, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * RC2
-     */
-    public static class RC2
-        extends JCEKeyGenerator
-    {
-        public RC2()
-        {
-            super("RC2", 128, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * RC4
-     */
-    public static class RC4
-        extends JCEKeyGenerator
-    {
-        public RC4()
-        {
-            super("RC4", 128, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * RC5
-     */
-    public static class RC5
-        extends JCEKeyGenerator
-    {
-        public RC5()
-        {
-            super("RC5", 128, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * RC5
-     */
-    public static class RC564
-        extends JCEKeyGenerator
-    {
-        public RC564()
-        {
-            super("RC5-64", 256, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * RC6
-     */
-    public static class RC6
-        extends JCEKeyGenerator
-    {
-        public RC6()
-        {
-            super("RC6", 256, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * AES
-     */
-    public static class AES
-        extends JCEKeyGenerator
-    {
-        public AES()
-        {
-            super("AES", 192, new CipherKeyGenerator());
-        }
-    }
-
-    public static class AES128
-        extends JCEKeyGenerator
-    {
-        public AES128()
-        {
-            super("AES", 128, new CipherKeyGenerator());
-        }
-    }
-
-    public static class AES192
-        extends JCEKeyGenerator
-    {
-        public AES192()
-        {
-            super("AES", 192, new CipherKeyGenerator());
-        }
-    }
-
-    public static class AES256
-        extends JCEKeyGenerator
-    {
-        public AES256()
-        {
-            super("AES", 256, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * GOST28147
-     */
-    public static class GOST28147
-        extends JCEKeyGenerator
-    {
-        public GOST28147()
-        {
-            super("GOST28147", 256, new CipherKeyGenerator());
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * generate a desEDE key in the a-b-c format.
+    //  */
+    // public static class DESede3
+    //     extends JCEKeyGenerator
+    // {
+    //     public DESede3()
+    //     {
+    //         super("DESede3", 192, new DESedeKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * SKIPJACK
+    //  */
+    // public static class Skipjack
+    //     extends JCEKeyGenerator
+    // {
+    //     public Skipjack()
+    //     {
+    //         super("SKIPJACK", 80, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * Blowfish
+    //  */
+    // public static class Blowfish
+    //     extends JCEKeyGenerator
+    // {
+    //     public Blowfish()
+    //     {
+    //         super("Blowfish", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * Twofish
+    //  */
+    // public static class Twofish
+    //     extends JCEKeyGenerator
+    // {
+    //     public Twofish()
+    //     {
+    //         super("Twofish", 256, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC2
+    //  */
+    // public static class RC2
+    //     extends JCEKeyGenerator
+    // {
+    //     public RC2()
+    //     {
+    //         super("RC2", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC4
+    //  */
+    // public static class RC4
+    //     extends JCEKeyGenerator
+    // {
+    //     public RC4()
+    //     {
+    //         super("RC4", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC5
+    //  */
+    // public static class RC5
+    //     extends JCEKeyGenerator
+    // {
+    //     public RC5()
+    //     {
+    //         super("RC5", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC5
+    //  */
+    // public static class RC564
+    //     extends JCEKeyGenerator
+    // {
+    //     public RC564()
+    //     {
+    //         super("RC5-64", 256, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * RC6
+    //  */
+    // public static class RC6
+    //     extends JCEKeyGenerator
+    // {
+    //     public RC6()
+    //     {
+    //         super("RC6", 256, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * GOST28147
+    //  */
+    // public static class GOST28147
+    //     extends JCEKeyGenerator
+    // {
+    //     public GOST28147()
+    //     {
+    //         super("GOST28147", 256, new CipherKeyGenerator());
+    //     }
+    // }
     
-    /**
-     * Rijndael
-     */
-    public static class Rijndael
-        extends JCEKeyGenerator
-    {
-        public Rijndael()
-        {
-            super("Rijndael", 192, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * Serpent
-     */
-    public static class Serpent
-        extends JCEKeyGenerator
-    {
-        public Serpent()
-        {
-            super("Serpent", 192, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * Camellia
-     */
-    public static class Camellia
-        extends JCEKeyGenerator
-    {
-        public Camellia()
-        {
-            super("Camellia", 256, new CipherKeyGenerator());
-        }
-    }
-    
-    /**
-     * CAST5
-     */
-    public static class CAST5
-        extends JCEKeyGenerator
-    {
-        public CAST5()
-        {
-            super("CAST5", 128, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * CAST6
-     */
-    public static class CAST6
-        extends JCEKeyGenerator
-    {
-        public CAST6()
-        {
-            super("CAST6", 256, new CipherKeyGenerator());
-        }
-    }
-
-    /**
-     * IDEA
-     */
-    public static class IDEA
-        extends JCEKeyGenerator
-    {
-        public IDEA()
-        {
-            super("IDEA", 128, new CipherKeyGenerator());
-        }
-    }
+    // /**
+    //  * Rijndael
+    //  */
+    // public static class Rijndael
+    //     extends JCEKeyGenerator
+    // {
+    //     public Rijndael()
+    //     {
+    //         super("Rijndael", 192, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * Serpent
+    //  */
+    // public static class Serpent
+    //     extends JCEKeyGenerator
+    // {
+    //     public Serpent()
+    //     {
+    //         super("Serpent", 192, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    //
+    //
+    // /**
+    //  * CAST6
+    //  */
+    // public static class CAST6
+    //     extends JCEKeyGenerator
+    // {
+    //     public CAST6()
+    //     {
+    //         super("CAST6", 256, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * TEA
+    //  */
+    // public static class TEA
+    //     extends JCEKeyGenerator
+    // {
+    //     public TEA()
+    //     {
+    //         super("TEA", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * XTEA
+    //  */
+    // public static class XTEA
+    //     extends JCEKeyGenerator
+    // {
+    //     public XTEA()
+    //     {
+    //         super("XTEA", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * Salsa20
+    //  */
+    // public static class Salsa20
+    //     extends JCEKeyGenerator
+    // {
+    //     public Salsa20()
+    //     {
+    //         super("Salsa20", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * HC128
+    //  */
+    // public static class HC128
+    //     extends JCEKeyGenerator
+    // {
+    //     public HC128()
+    //     {
+    //         super("HC128", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * HC256
+    //  */
+    // public static class HC256
+    //     extends JCEKeyGenerator
+    // {
+    //     public HC256()
+    //     {
+    //         super("HC256", 256, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * VMPC
+    //  */
+    // public static class VMPC
+    //     extends JCEKeyGenerator
+    // {
+    //     public VMPC()
+    //     {
+    //         super("VMPC", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    //
+    // /**
+    //  * VMPC-KSA3
+    //  */
+    // public static class VMPCKSA3
+    //     extends JCEKeyGenerator
+    // {
+    //     public VMPCKSA3()
+    //     {
+    //         super("VMPC-KSA3", 128, new CipherKeyGenerator());
+    //     }
+    // }
+    // END android-removed
 
     // HMAC Related secret keys..
   
@@ -414,21 +403,21 @@
     //         super("HMACMD2", 128, new CipherKeyGenerator());
     //     }
     // }
+    //
+    //
+    // /**
+    //  * MD4HMAC
+    //  */
+    // public static class MD4HMAC
+    //     extends JCEKeyGenerator
+    // {
+    //     public MD4HMAC()
+    //     {
+    //         super("HMACMD4", 128, new CipherKeyGenerator());
+    //     }
+    // }
     // END android-removed
 
-
-    /**
-     * MD4HMAC
-     */
-    public static class MD4HMAC
-        extends JCEKeyGenerator
-    {
-        public MD4HMAC()
-        {
-            super("HMACMD4", 128, new CipherKeyGenerator());
-        }
-    }
-
     /**
      * MD5HMAC
      */
@@ -442,29 +431,29 @@
     }
 
 
-    /**
-     * RIPE128HMAC
-     */
-    public static class RIPEMD128HMAC
-        extends JCEKeyGenerator
-    {
-        public RIPEMD128HMAC()
-        {
-            super("HMACRIPEMD128", 128, new CipherKeyGenerator());
-        }
-    }
+    // /**
+    //  * RIPE128HMAC
+    //  */
+    // public static class RIPEMD128HMAC
+    //     extends JCEKeyGenerator
+    // {
+    //     public RIPEMD128HMAC()
+    //     {
+    //         super("HMACRIPEMD128", 128, new CipherKeyGenerator());
+    //     }
+    // }
 
-    /**
-     * RIPE160HMAC
-     */
-    public static class RIPEMD160HMAC
-        extends JCEKeyGenerator
-    {
-        public RIPEMD160HMAC()
-        {
-            super("HMACRIPEMD160", 160, new CipherKeyGenerator());
-        }
-    }
+    // /**
+    //  * RIPE160HMAC
+    //  */
+    // public static class RIPEMD160HMAC
+    //     extends JCEKeyGenerator
+    // {
+    //     public RIPEMD160HMAC()
+    //     {
+    //         super("HMACRIPEMD160", 160, new CipherKeyGenerator());
+    //     }
+    // }
 
 
     /**
@@ -479,17 +468,19 @@
         }
     }
 
-    /**
-     * HMACSHA224
-     */
-    public static class HMACSHA224
-        extends JCEKeyGenerator
-    {
-        public HMACSHA224()
-        {
-            super("HMACSHA224", 224, new CipherKeyGenerator());
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * HMACSHA224
+    //  */
+    // public static class HMACSHA224
+    //     extends JCEKeyGenerator
+    // {
+    //     public HMACSHA224()
+    //     {
+    //         super("HMACSHA224", 224, new CipherKeyGenerator());
+    //     }
+    // }
+    // END android-removed
     
     /**
      * HMACSHA256
@@ -527,15 +518,17 @@
         }
     }
     
-    /**
-     * HMACTIGER
-     */
-    public static class HMACTIGER
-        extends JCEKeyGenerator
-    {
-        public HMACTIGER()
-        {
-            super("HMACTIGER", 192, new CipherKeyGenerator());
-        }
-    }
+    // BEGIN android-removed
+    // /**
+    //  * HMACTIGER
+    //  */
+    // public static class HMACTIGER
+    //     extends JCEKeyGenerator
+    // {
+    //     public HMACTIGER()
+    //     {
+    //         super("HMACTIGER", 192, new CipherKeyGenerator());
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEMac.java b/src/main/java/org/bouncycastle/jce/provider/JCEMac.java
index 74d8147..1c1e4ea 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCEMac.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCEMac.java
@@ -1,36 +1,56 @@
 package org.bouncycastle.jce.provider;
 
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.MacSpi;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEParameterSpec;
-
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Mac;
-import org.bouncycastle.crypto.digests.*;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.MD5Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.SHA1Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.TigerDigest;
+// END android-removed
 import org.bouncycastle.crypto.engines.DESEngine;
 import org.bouncycastle.crypto.engines.DESedeEngine;
 // BEGIN android-removed
-// import org.bouncycastle.crypto.engines.IDEAEngine;
 // import org.bouncycastle.crypto.engines.RC2Engine;
 // import org.bouncycastle.crypto.engines.RC532Engine;
 // import org.bouncycastle.crypto.engines.SkipjackEngine;
 // END android-removed
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
-import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
 // BEGIN android-removed
+// import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
 // import org.bouncycastle.crypto.macs.GOST28147Mac;
 // END android-removed
 import org.bouncycastle.crypto.macs.HMac;
-import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
-import org.bouncycastle.crypto.macs.OldHMac;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
+// import org.bouncycastle.crypto.macs.OldHMac;
+// import org.bouncycastle.crypto.macs.VMPCMac;
+// END android-removed
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 
+import javax.crypto.MacSpi;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
 public class JCEMac
     extends MacSpi implements PBE
 {
@@ -140,34 +160,34 @@
      * the classes that extend directly off us.
      */
 
-    /**
-     * DES
-     */
-    public static class DES
-        extends JCEMac
-    {
-        public DES()
-        {
-            super(new CBCBlockCipherMac(new DESEngine()));
-        }
-    }
-
-    /**
-     * DESede
-     */
-    public static class DESede
-        extends JCEMac
-    {
-        public DESede()
-        {
-            super(new CBCBlockCipherMac(new DESedeEngine()));
-        }
-    }
-
-    /**
-     * SKIPJACK
-     */
     // BEGIN android-removed
+    // /**
+    //  * DES
+    //  */
+    // public static class DES
+    //     extends JCEMac
+    // {
+    //     public DES()
+    //     {
+    //         super(new CBCBlockCipherMac(new DESEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * DESede
+    //  */
+    // public static class DESede
+    //     extends JCEMac
+    // {
+    //     public DESede()
+    //     {
+    //         super(new CBCBlockCipherMac(new DESedeEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * SKIPJACK
+    //  */
     // public static class Skipjack
     //     extends JCEMac
     // {
@@ -176,26 +196,10 @@
     //         super(new CBCBlockCipherMac(new SkipjackEngine()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * IDEA
-     */
-    // BEGIN android-removed
-    // public static class IDEA
-    //     extends JCEMac
-    // {
-    //     public IDEA()
-    //     {
-    //         super(new CBCBlockCipherMac(new IDEAEngine()));
-    //     }
-    // }
-    // END android-removed
-
-    /**
-     * RC2
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * RC2
+    //  */
     // public static class RC2
     //     extends JCEMac
     // {
@@ -204,12 +208,10 @@
     //         super(new CBCBlockCipherMac(new RC2Engine()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * RC5
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * RC5
+    //  */
     // public static class RC5
     //     extends JCEMac
     // {
@@ -218,12 +220,10 @@
     //         super(new CBCBlockCipherMac(new RC532Engine()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * GOST28147
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * GOST28147
+    //  */
     // public static class GOST28147
     //     extends JCEMac
     // {
@@ -232,36 +232,46 @@
     //         super(new GOST28147Mac());
     //     }
     // }
-    // END android-removed
-    
-    /**
-     * DES
-     */
-    public static class DESCFB8
-        extends JCEMac
-    {
-        public DESCFB8()
-        {
-            super(new CFBBlockCipherMac(new DESEngine()));
-        }
-    }
-
-    /**
-     * DESede
-     */
-    public static class DESedeCFB8
-        extends JCEMac
-    {
-        public DESedeCFB8()
-        {
-            super(new CFBBlockCipherMac(new DESedeEngine()));
-        }
-    }
-
-    /**
-     * SKIPJACK
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * VMPC
+    //  */
+    // public static class VMPC
+    //     extends JCEMac
+    // {
+    //     public VMPC()
+    //     {
+    //         super(new VMPCMac());
+    //     }
+    // }
+    //
+    // /**
+    //  * DES
+    //  */
+    // public static class DESCFB8
+    //     extends JCEMac
+    // {
+    //     public DESCFB8()
+    //     {
+    //         super(new CFBBlockCipherMac(new DESEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * DESede
+    //  */
+    // public static class DESedeCFB8
+    //     extends JCEMac
+    // {
+    //     public DESedeCFB8()
+    //     {
+    //         super(new CFBBlockCipherMac(new DESedeEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * SKIPJACK
+    //  */
     // public static class SkipjackCFB8
     //     extends JCEMac
     // {
@@ -270,26 +280,10 @@
     //         super(new CFBBlockCipherMac(new SkipjackEngine()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * IDEACFB8
-     */
-    // BEGIN android-removed
-    // public static class IDEACFB8
-    //     extends JCEMac
-    // {
-    //     public IDEACFB8()
-    //     {
-    //         super(new CFBBlockCipherMac(new IDEAEngine()));
-    //     }
-    // }
-    // END android-removed
-
-    /**
-     * RC2CFB8
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * RC2CFB8
+    //  */
     // public static class RC2CFB8
     //     extends JCEMac
     // {
@@ -298,12 +292,10 @@
     //         super(new CFBBlockCipherMac(new RC2Engine()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * RC5CFB8
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * RC5CFB8
+    //  */
     // public static class RC5CFB8
     //     extends JCEMac
     // {
@@ -312,37 +304,59 @@
     //         super(new CFBBlockCipherMac(new RC532Engine()));
     //     }
     // }
-    // END android-removed
-    
-    
-    /**
-     * DESede64
-     */
-    public static class DESede64
-        extends JCEMac
-    {
-        public DESede64()
-        {
-            super(new CBCBlockCipherMac(new DESedeEngine(), 64));
-        }
-    }
-    
-    /**
-     * DES9797Alg3
-     */
-    public static class DES9797Alg3
-        extends JCEMac
-    {
-        public DES9797Alg3()
-        {
-            super(new ISO9797Alg3Mac(new DESEngine()));
-        }
-    }
-
-    /**
-     * MD2 HMac
-     */
-    // BEGIN android-removed
+    //
+    //
+    // /**
+    //  * DESede64
+    //  */
+    // public static class DESede64
+    //     extends JCEMac
+    // {
+    //     public DESede64()
+    //     {
+    //         super(new CBCBlockCipherMac(new DESedeEngine(), 64));
+    //     }
+    // }
+    //
+    // /**
+    //  * DESede64with7816-4Padding
+    //  */
+    // public static class DESede64with7816d4
+    //     extends JCEMac
+    // {
+    //     public DESede64with7816d4()
+    //     {
+    //         super(new CBCBlockCipherMac(new DESedeEngine(), 64, new ISO7816d4Padding()));
+    //     }
+    // }
+    //
+    // /**
+    //  * DES9797Alg3with7816-4Padding
+    //  */
+    // public static class DES9797Alg3with7816d4
+    //     extends JCEMac
+    // {
+    //     public DES9797Alg3with7816d4()
+    //     {
+    //         super(new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding()));
+    //     }
+    // }
+    //
+    // /**
+    //  * DES9797Alg3
+    //  */
+    // public static class DES9797Alg3
+    //     extends JCEMac
+    // {
+    //     public DES9797Alg3()
+    //     {
+    //         super(new ISO9797Alg3Mac(new DESEngine()));
+    //     }
+    // }
+    //
+    // /**
+    //  * MD2 HMac
+    //  */
     // public static class MD2
     //     extends JCEMac
     // {
@@ -351,12 +365,10 @@
     //         super(new HMac(new MD2Digest()));
     //     }
     // }
-    // END android-removed
-
-    /**
-     * MD4 HMac
-     */
-    // BEGIN android-removed
+    //
+    // /**
+    //  * MD4 HMac
+    //  */
     // public static class MD4
     //     extends JCEMac
     // {
@@ -366,7 +378,7 @@
     //     }
     // }
     // END android-removed
-
+    
     /**
      * MD5 HMac
      */
@@ -378,7 +390,7 @@
             super(new HMac(new MD5Digest()));
         }
     }
-
+    
     /**
      * SHA1 HMac
      */
@@ -390,18 +402,20 @@
             super(new HMac(new SHA1Digest()));
         }
     }
-
-    /**
-     * SHA-224 HMac
-     */
-    public static class SHA224
-        extends JCEMac
-    {
-        public SHA224()
-        {
-            super(new HMac(new SHA224Digest()));
-        }
-    }
+    
+    // BEGIN android-removed
+    // /**
+    //  * SHA-224 HMac
+    //  */
+    // public static class SHA224
+    //     extends JCEMac
+    // {
+    //     public SHA224()
+    //     {
+    //         super(new HMac(new SHA224Digest()));
+    //     }
+    // }
+    // END android-removed
     
     /**
      * SHA-256 HMac
@@ -414,7 +428,7 @@
             super(new HMac(new SHA256Digest()));
         }
     }
-
+    
     /**
      * SHA-384 HMac
      */
@@ -426,15 +440,17 @@
             super(new HMac(new SHA384Digest()));
         }
     }
-
-    public static class OldSHA384
-        extends JCEMac
-    {
-        public OldSHA384()
-        {
-            super(new OldHMac(new SHA384Digest()));
-        }
-    }
+    
+    // BEGIN android-removed
+    // public static class OldSHA384
+    //     extends JCEMac
+    // {
+    //     public OldSHA384()
+    //     {
+    //         super(new OldHMac(new SHA384Digest()));
+    //     }
+    // }
+    // END android-removed
     
     /**
      * SHA-512 HMac
@@ -447,98 +463,96 @@
             super(new HMac(new SHA512Digest()));
         }
     }
-
-    /**
-     * SHA-512 HMac
-     */
-    public static class OldSHA512
-        extends JCEMac
-    {
-        public OldSHA512()
-        {
-            super(new OldHMac(new SHA512Digest()));
-        }
-    }
     
-// BEGIN android-removed
-//    /**
-//     * RIPEMD128 HMac
-//     */
-//    public static class RIPEMD128
-//        extends JCEMac
-//    {
-//        public RIPEMD128()
-//        {
-//           super(new HMac(new RIPEMD128Digest()));
-//        }
-//    }
-//
-//    /**
-//     * RIPEMD160 HMac
-//     */
-//    public static class RIPEMD160
-//        extends JCEMac
-//    {
-//        public RIPEMD160()
-//        {
-//           super(new HMac(new RIPEMD160Digest()));
-//        }
-//    }
-//
-//    /**
-//     * Tiger HMac
-//     */
-//    public static class Tiger
-//        extends JCEMac
-//    {
-//        public Tiger()
-//        {
-//            super(new HMac(new TigerDigest()));
-//        }
-//    }
-//
-//    //
-//    // PKCS12 states that the same algorithm should be used
-//    // for the key generation as is used in the HMAC, so that
-//    // is what we do here.
-//    //
-//
-//    /**
-//     * PBEWithHmacRIPEMD160
-//     */
-//    public static class PBEWithRIPEMD160
-//        extends JCEMac
-//    {
-//        public PBEWithRIPEMD160()
-//        {
-//            super(new HMac(new RIPEMD160Digest()), PKCS12, RIPEMD160, 160);
-//        }
-//    }
-// END android-removed
-
-    /**
-     * PBEWithHmacSHA
-     */
-    public static class PBEWithSHA
-        extends JCEMac
-    {
-        public PBEWithSHA()
-        {
-            super(new HMac(new SHA1Digest()), PKCS12, SHA1, 160);
-        }
-    }
-
-    /**
-     * PBEWithHmacTiger
-     */
-// BEGIN android-removed
-//    public static class PBEWithTiger
-//        extends JCEMac
-//    {
-//        public PBEWithTiger()
-//        {
-//            super(new HMac(new TigerDigest()), PKCS12, TIGER, 192);
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // /**
+    //  * SHA-512 HMac
+    //  */
+    // public static class OldSHA512
+    //     extends JCEMac
+    // {
+    //     public OldSHA512()
+    //     {
+    //         super(new OldHMac(new SHA512Digest()));
+    //     }
+    // }
+    //
+    // /**
+    //  * RIPEMD128 HMac
+    //  */
+    // public static class RIPEMD128
+    //     extends JCEMac
+    // {
+    //     public RIPEMD128()
+    //     {
+    //        super(new HMac(new RIPEMD128Digest()));
+    //     }
+    // }
+    //
+    // /**
+    //  * RIPEMD160 HMac
+    //  */
+    // public static class RIPEMD160
+    //     extends JCEMac
+    // {
+    //     public RIPEMD160()
+    //     {
+    //        super(new HMac(new RIPEMD160Digest()));
+    //     }
+    // }
+    //
+    // /**
+    //  * Tiger HMac
+    //  */
+    // public static class Tiger
+    //     extends JCEMac
+    // {
+    //     public Tiger()
+    //     {
+    //         super(new HMac(new TigerDigest()));
+    //     }
+    // }
+    //
+    // //
+    // // PKCS12 states that the same algorithm should be used
+    // // for the key generation as is used in the HMAC, so that
+    // // is what we do here.
+    // //
+    //
+    // /**
+    //  * PBEWithHmacRIPEMD160
+    //  */
+    // public static class PBEWithRIPEMD160
+    //     extends JCEMac
+    // {
+    //     public PBEWithRIPEMD160()
+    //     {
+    //         super(new HMac(new RIPEMD160Digest()), PKCS12, RIPEMD160, 160);
+    //     }
+    // }
+    //
+    //  /**
+    //   * PBEWithHmacSHA
+    //   */
+    //  public static class PBEWithSHA
+    //      extends JCEMac
+    //  {
+    //      public PBEWithSHA()
+    //      {
+    //          super(new HMac(new SHA1Digest()), PKCS12, SHA1, 160);
+    //      }
+    //  }
+    //
+    //  /**
+    //   * PBEWithHmacTiger
+    //   */
+    // public static class PBEWithTiger
+    //     extends JCEMac
+    // {
+    //     public PBEWithTiger()
+    //     {
+    //         super(new HMac(new TigerDigest()), PKCS12, TIGER, 192);
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCERSACipher.java b/src/main/java/org/bouncycastle/jce/provider/JCERSACipher.java
index 54b49c0..877dc6b 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCERSACipher.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCERSACipher.java
@@ -1,5 +1,23 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
+import org.bouncycastle.crypto.encodings.OAEPEncoding;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Strings;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
 import java.io.ByteArrayOutputStream;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
@@ -14,25 +32,6 @@
 import java.security.spec.InvalidParameterSpecException;
 import java.security.spec.MGF1ParameterSpec;
 
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.OAEPParameterSpec;
-import javax.crypto.spec.PSource;
-
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
-import org.bouncycastle.crypto.encodings.OAEPEncoding;
-import org.bouncycastle.crypto.encodings.PKCS1Encoding;
-import org.bouncycastle.crypto.engines.RSAEngine;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.util.Strings;
-
 public class JCERSACipher extends WrapCipherSpi
 {
     private AsymmetricBlockCipher   cipher;
@@ -83,7 +82,7 @@
             throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm());
         }
 
-        cipher = new OAEPEncoding(new RSAEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue());        
+        cipher = new OAEPEncoding(new RSABlindedEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue());
         paramSpec = pSpec;
     }
     
@@ -192,15 +191,15 @@
 
         if (pad.equals("NOPADDING"))
         {
-            cipher = new RSAEngine();
+            cipher = new RSABlindedEngine();
         }
         else if (pad.equals("PKCS1PADDING"))
         {
-            cipher = new PKCS1Encoding(new RSAEngine());
+            cipher = new PKCS1Encoding(new RSABlindedEngine());
         }
         else if (pad.equals("ISO9796-1PADDING"))
         {
-            cipher = new ISO9796d1Encoding(new RSAEngine());
+            cipher = new ISO9796d1Encoding(new RSABlindedEngine());
         }
         else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING"))
         {
@@ -210,23 +209,23 @@
         {
             initFromSpec(OAEPParameterSpec.DEFAULT);
         }
-        else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING"))
+        else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-1ANDMGF1PADDING"))
         {
             initFromSpec(OAEPParameterSpec.DEFAULT);
         }
-        else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING"))
+        else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-224ANDMGF1PADDING"))
         {
             initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT));
         }
-        else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING"))
+        else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-256ANDMGF1PADDING"))
         {
             initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
         }
-        else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING"))
+        else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-384ANDMGF1PADDING"))
         {
             initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT));
         }
-        else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING"))
+        else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-512ANDMGF1PADDING"))
         {
             initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT));
         }
@@ -287,22 +286,23 @@
                 {
                     throw new InvalidAlgorithmParameterException("unkown MGF parameters");
                 }
-                
-                MGF1ParameterSpec   mgfParams = (MGF1ParameterSpec)spec.getMGFParameters();
-                
-                if (!JCEDigestUtil.isSameDigest(mgfParams.getDigestAlgorithm(), spec.getDigestAlgorithm()))
-                {
-                    throw new InvalidAlgorithmParameterException("digest algorithm for MGF should be the same as for OAEP parameters.");
-                }
-                
-                Digest              digest = JCEDigestUtil.getDigest(mgfParams.getDigestAlgorithm());
-                
+    
+                Digest digest = JCEDigestUtil.getDigest(spec.getDigestAlgorithm());
+
                 if (digest == null)
                 {
+                    throw new InvalidAlgorithmParameterException("no match on digest algorithm: "+ spec.getDigestAlgorithm());
+                }
+
+                MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)spec.getMGFParameters();
+                Digest mgfDigest = JCEDigestUtil.getDigest(mgfParams.getDigestAlgorithm());
+                
+                if (mgfDigest == null)
+                {
                     throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm());
                 }
                 
-                cipher = new OAEPEncoding(new RSAEngine(), digest, ((PSource.PSpecified)spec.getPSource()).getValue());
+                cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue());
             }
         }
         else
@@ -310,7 +310,7 @@
             throw new IllegalArgumentException("unknown parameter type.");
         }
 
-        if (!(cipher instanceof RSAEngine))
+        if (!(cipher instanceof RSABlindedEngine))
         {
             if (random != null)
             {
@@ -386,7 +386,7 @@
     {
         bOut.write(input, inputOffset, inputLen);
 
-        if (cipher instanceof RSAEngine)
+        if (cipher instanceof RSABlindedEngine)
         {
             if (bOut.size() > cipher.getInputBlockSize() + 1)
             {
@@ -413,7 +413,7 @@
     {
         bOut.write(input, inputOffset, inputLen);
 
-        if (cipher instanceof RSAEngine)
+        if (cipher instanceof RSABlindedEngine)
         {
             if (bOut.size() > cipher.getInputBlockSize() + 1)
             {
@@ -442,7 +442,7 @@
             bOut.write(input, inputOffset, inputLen);
         }
 
-        if (cipher instanceof RSAEngine)
+        if (cipher instanceof RSABlindedEngine)
         {
             if (bOut.size() > cipher.getInputBlockSize() + 1)
             {
@@ -484,7 +484,7 @@
             bOut.write(input, inputOffset, inputLen);
         }
 
-        if (cipher instanceof RSAEngine)
+        if (cipher instanceof RSABlindedEngine)
         {
             if (bOut.size() > cipher.getInputBlockSize() + 1)
             {
@@ -530,52 +530,54 @@
     {
         public NoPadding()
         {
-            super(new RSAEngine());
+            super(new RSABlindedEngine());
         }
     }
 
-    static public class PKCS1v1_5Padding
-        extends JCERSACipher
-    {
-        public PKCS1v1_5Padding()
-        {
-            super(new PKCS1Encoding(new RSAEngine()));
-        }
-    }
-
-    static public class PKCS1v1_5Padding_PrivateOnly
-        extends JCERSACipher
-    {
-        public PKCS1v1_5Padding_PrivateOnly()
-        {
-            super(false, true, new PKCS1Encoding(new RSAEngine()));
-        }
-    }
-
-    static public class PKCS1v1_5Padding_PublicOnly
-        extends JCERSACipher
-    {
-        public PKCS1v1_5Padding_PublicOnly()
-        {
-            super(true, false, new PKCS1Encoding(new RSAEngine()));
-        }
-    }
-
-    static public class OAEPPadding
-        extends JCERSACipher
-    {
-        public OAEPPadding()
-        {
-            super(OAEPParameterSpec.DEFAULT);
-        }
-    }
-    
-    static public class ISO9796d1Padding
-        extends JCERSACipher
-    {
-        public ISO9796d1Padding()
-        {
-            super(new ISO9796d1Encoding(new RSAEngine()));
-        }
-    }
+    // BEGIN android-removed
+    // static public class PKCS1v1_5Padding
+    //     extends JCERSACipher
+    // {
+    //     public PKCS1v1_5Padding()
+    //     {
+    //         super(new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class PKCS1v1_5Padding_PrivateOnly
+    //     extends JCERSACipher
+    // {
+    //     public PKCS1v1_5Padding_PrivateOnly()
+    //     {
+    //         super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class PKCS1v1_5Padding_PublicOnly
+    //     extends JCERSACipher
+    // {
+    //     public PKCS1v1_5Padding_PublicOnly()
+    //     {
+    //         super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class OAEPPadding
+    //     extends JCERSACipher
+    // {
+    //     public OAEPPadding()
+    //     {
+    //         super(OAEPParameterSpec.DEFAULT);
+    //     }
+    // }
+    //
+    // static public class ISO9796d1Padding
+    //     extends JCERSACipher
+    // {
+    //     public ISO9796d1Padding()
+    //     {
+    //         super(new ISO9796d1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java b/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
index 0971e90..c7e2794 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
@@ -1,20 +1,17 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.spec.RSAPrivateCrtKeySpec;
-
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 
+import java.math.BigInteger;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+
 /**
  * A provider representation for a RSA private key, with CRT factors included.
  */
@@ -22,6 +19,8 @@
     extends JCERSAPrivateKey
     implements RSAPrivateCrtKey
 {
+    static final long serialVersionUID = 7834723820638524718L;
+    
     private BigInteger  publicExponent;
     private BigInteger  primeP;
     private BigInteger  primeQ;
@@ -127,7 +126,7 @@
     public byte[] getEncoded()
     {
         // BEGIN android-changed
-        PrivateKeyInfo          info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.THE_ONE), new RSAPrivateKeyStructure(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient()).getDERObject());
+        PrivateKeyInfo          info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKeyStructure(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient()).getDERObject());
         // END android-changed
 
         return info.getDEREncoded();
@@ -193,18 +192,25 @@
         return crtCoefficient;
     }
 
+    public int hashCode()
+    {
+        return this.getModulus().hashCode()
+               ^ this.getPublicExponent().hashCode()
+               ^ this.getPrivateExponent().hashCode();
+    }
+
     public boolean equals(Object o)
     {
-        if (!(o instanceof RSAPrivateCrtKey))
-        {
-            return false;
-        }
-
         if (o == this)
         {
             return true;
         }
 
+        if (!(o instanceof RSAPrivateCrtKey))
+        {
+            return false;
+        }
+
         RSAPrivateCrtKey key = (RSAPrivateCrtKey)o;
 
         return this.getModulus().equals(key.getModulus())
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java b/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
index 74b8d08..bbead9f 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
@@ -1,6 +1,15 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -8,24 +17,18 @@
 import java.security.interfaces.RSAPrivateKey;
 import java.security.spec.RSAPrivateKeySpec;
 import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OutputStream;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 
 public class JCERSAPrivateKey
     implements RSAPrivateKey, PKCS12BagAttributeCarrier
 {
+    static final long serialVersionUID = 5110188922551353628L;
+
+    private static BigInteger ZERO = BigInteger.valueOf(0);
+
     protected BigInteger modulus;
     protected BigInteger privateExponent;
 
-    private Hashtable   pkcs12Attributes = new Hashtable();
-    private Vector      pkcs12Ordering = new Vector();
+    private PKCS12BagAttributeCarrierImpl   attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
     protected JCERSAPrivateKey()
     {
@@ -69,12 +72,16 @@
 
     public String getFormat()
     {
-        return "NULL";
+        return "PKCS#8";
     }
 
     public byte[] getEncoded()
     {
-        return null;
+        // BEGIN android-changed
+        PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKeyStructure(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO).getDERObject());
+        // END android-changed
+
+        return info.getDEREncoded();
     }
 
     public boolean equals(Object o)
@@ -104,19 +111,18 @@
         DERObjectIdentifier oid,
         DEREncodable        attribute)
     {
-        pkcs12Attributes.put(oid, attribute);
-        pkcs12Ordering.addElement(oid);
+        attrCarrier.setBagAttribute(oid, attribute);
     }
 
     public DEREncodable getBagAttribute(
         DERObjectIdentifier oid)
     {
-        return (DEREncodable)pkcs12Attributes.get(oid);
+        return attrCarrier.getBagAttribute(oid);
     }
 
     public Enumeration getBagAttributeKeys()
     {
-        return pkcs12Ordering.elements();
+        return attrCarrier.getBagAttributeKeys();
     }
 
     private void readObject(
@@ -124,28 +130,9 @@
         throws IOException, ClassNotFoundException
     {
         this.modulus = (BigInteger)in.readObject();
-
-        Object  obj = in.readObject();
-
-        if (obj instanceof Hashtable)
-        {
-            this.pkcs12Attributes = (Hashtable)obj;
-            this.pkcs12Ordering = (Vector)in.readObject();
-        }
-        else
-        {
-            this.pkcs12Attributes = new Hashtable();
-            this.pkcs12Ordering = new Vector();
-
-            ASN1InputStream         aIn = new ASN1InputStream((byte[])obj);
-
-            DERObjectIdentifier    oid;
-
-            while ((oid = (DERObjectIdentifier)aIn.readObject()) != null)
-            {
-                this.setBagAttribute(oid, aIn.readObject());
-            }
-        }
+        this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+        
+        attrCarrier.readObject(in);
 
         this.privateExponent = (BigInteger)in.readObject();
     }
@@ -156,28 +143,7 @@
     {
         out.writeObject(modulus);
 
-        if (pkcs12Ordering.size() == 0)
-        {
-            out.writeObject(pkcs12Attributes);
-            out.writeObject(pkcs12Ordering);
-        }
-        else
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
-
-            Enumeration             e = this.getBagAttributeKeys();
-
-            while (e.hasMoreElements())
-            {
-                DEREncodable    oid = (DEREncodable)e.nextElement();
-
-                aOut.writeObject(oid);
-                aOut.writeObject(pkcs12Attributes.get(oid));
-            }
-
-            out.writeObject(bOut.toByteArray());
-        }
+        attrCarrier.writeObject(out);
 
         out.writeObject(privateExponent);
     }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java b/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java
index e546323..ea601ed 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java
@@ -1,23 +1,23 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPublicKeySpec;
-
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
 public class JCERSAPublicKey
     implements RSAPublicKey
 {
+    static final long serialVersionUID = 2675817738516720772L;
+    
     private BigInteger modulus;
     private BigInteger publicExponent;
 
@@ -91,24 +91,29 @@
     public byte[] getEncoded()
     {
         // BEGIN android-changed
-        SubjectPublicKeyInfo    info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.THE_ONE), new RSAPublicKeyStructure(getModulus(), getPublicExponent()).getDERObject());
+        SubjectPublicKeyInfo    info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKeyStructure(getModulus(), getPublicExponent()).getDERObject());
         // END android-changed
 
         return info.getDEREncoded();
     }
 
+    public int hashCode()
+    {
+        return this.getModulus().hashCode() ^ this.getPublicExponent().hashCode();
+    }
+
     public boolean equals(Object o)
     {
-        if (!(o instanceof RSAPublicKey))
-        {
-            return false;
-        }
-
         if (o == this)
         {
             return true;
         }
 
+        if (!(o instanceof RSAPublicKey))
+        {
+            return false;
+        }
+
         RSAPublicKey key = (RSAPublicKey)o;
 
         return getModulus().equals(key.getModulus())
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java b/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
index 0a45b74..3bdf4a1 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
@@ -321,6 +321,32 @@
         }
     }
     
+    // BEGIN android-removed
+    // /**
+    //  * PBEWithMD2AndDES
+    //  */
+    // static public class PBEWithMD2AndDES
+    //     extends DESPBEKeyFactory
+    // {
+    //     public PBEWithMD2AndDES()
+    //     {
+    //         super("PBEwithMD2andDES", PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, true, PKCS5S1, MD2, 64, 64);
+    //     }
+    // }
+    //
+    // /**
+    //  * PBEWithMD2AndRC2
+    //  */
+    // static public class PBEWithMD2AndRC2
+    //     extends PBEKeyFactory
+    // {
+    //     public PBEWithMD2AndRC2()
+    //     {
+    //         super("PBEwithMD2andRC2", PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, true, PKCS5S1, MD2, 64, 64);
+    //     }
+    // }
+    // END android-removed
+
    /**
     * PBEWithMD5AndDES
     */
@@ -329,45 +355,46 @@
    {
        public PBEWithMD5AndDES()
        {
-           super("PBEwithMD5andDES", null, true, PKCS5S1, MD5, 64, 64);
+           super("PBEwithMD5andDES", PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, true, PKCS5S1, MD5, 64, 64);
        }
    }
 
-   /**
-    * PBEWithMD5AndRC2
-    */
-   static public class PBEWithMD5AndRC2
-       extends PBEKeyFactory
-   {
-       public PBEWithMD5AndRC2()
-       {
-           super("PBEwithMD5andRC2", null, true, PKCS5S1, MD5, 64, 64);
-       }
-   }
-
-   /**
-    * PBEWithSHA1AndDES
-    */
-   static public class PBEWithSHA1AndDES
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA1AndDES()
-       {
-           super("PBEwithSHA1andDES", null, true, PKCS5S1, SHA1, 64, 64);
-       }
-   }
-
-   /**
-    * PBEWithSHA1AndRC2
-    */
-   static public class PBEWithSHA1AndRC2
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA1AndRC2()
-       {
-           super("PBEwithSHA1andRC2", null, true, PKCS5S1, SHA1, 64, 64);
-       }
-   }
+   // BEGIN android-removed
+   // /**
+   //  * PBEWithMD5AndRC2
+   //  */
+   // static public class PBEWithMD5AndRC2
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithMD5AndRC2()
+   //     {
+   //         super("PBEwithMD5andRC2", PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, true, PKCS5S1, MD5, 64, 64);
+   //     }
+   // }
+   // /**
+   //  * PBEWithSHA1AndDES
+   //  */
+   // static public class PBEWithSHA1AndDES
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA1AndDES()
+   //     {
+   //         super("PBEwithSHA1andDES", PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, true, PKCS5S1, SHA1, 64, 64);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA1AndRC2
+   //  */
+   // static public class PBEWithSHA1AndRC2
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA1AndRC2()
+   //     {
+   //         super("PBEwithSHA1andRC2", PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, true, PKCS5S1, SHA1, 64, 64);
+   //     }
+   // }
+   // END android-removed
 
    /**
     * PBEWithSHAAnd3-KeyTripleDES-CBC
@@ -381,202 +408,186 @@
        }
    }
 
-   /**
-    * PBEWithSHAAnd2-KeyTripleDES-CBC
-    */
-   static public class PBEWithSHAAndDES2Key
-       extends PBEKeyFactory
-   {
-       public PBEWithSHAAndDES2Key()
-       {
-           super("PBEwithSHAandDES2Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, true, PKCS12, SHA1, 128, 64);
-       }
-   }
-// BEGIN android-removed
-//   /**
-//    * PBEWithSHAAnd128BitRC2-CBC
-//    */
-//   static public class PBEWithSHAAnd128BitRC2
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithSHAAnd128BitRC2()
-//       {
-//           super("PBEwithSHAand128BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, true, PKCS12, SHA1, 128, 64);
-//       }
-//   }
-// END android-removed
-
-   /**
-    * PBEWithSHAAnd40BitRC2-CBC
-    */
-   static public class PBEWithSHAAnd40BitRC2
-       extends PBEKeyFactory
-   {
-       public PBEWithSHAAnd40BitRC2()
-       {
-           super("PBEwithSHAand40BitRC2-CBC", PKCSObjectIdentifiers.pbewithSHAAnd40BitRC2_CBC, true, PKCS12, SHA1, 40, 64);
-       }
-   }
-
-// BEGIN android-removed
-//   /**
-//    * PBEWithSHAAndTwofish-CBC
-//    */
-//   static public class PBEWithSHAAndTwofish
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithSHAAndTwofish()
-//       {
-//           super("PBEwithSHAandTwofish-CBC", null, true, PKCS12, SHA1, 256, 128);
-//       }
-//   }
-//
-//   /**
-//    * PBEWithSHAAndIDEA-CBC
-//    */
-//   static public class PBEWithSHAAndIDEA
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithSHAAndIDEA()
-//       {
-//           super("PBEwithSHAandIDEA-CBC", null, true, PKCS12, SHA1, 128, 64);
-//       }
-//   }
-//
-//   /**
-//    * PBEWithSHAAnd128BitRC4
-//    */
-//   static public class PBEWithSHAAnd128BitRC4
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithSHAAnd128BitRC4()
-//       {
-//           super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 128, 0);
-//       }
-//   }
-//
-//   /**
-//    * PBEWithSHAAnd40BitRC4
-//    */
-//   static public class PBEWithSHAAnd40BitRC4
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithSHAAnd40BitRC4()
-//       {
-//           super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 40, 0);
-//       }
-//   }
-//   
-//   /**
-//    * PBEWithHmacRIPEMD160
-//    */
-//   public static class PBEWithRIPEMD160
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithRIPEMD160()
-//       {
-//           super("PBEwithHmacRIPEMD160", null, false, PKCS12, RIPEMD160, 160, 0);
-//       }
-//   }
-// END android-removed
-
-   /**
-    * PBEWithHmacSHA
-    */
-   public static class PBEWithSHA
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA()
-       {
-           super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
-       }
-   }
-
-// BEGIN android-removed
-//   /**
-//    * PBEWithHmacTiger
-//    */
-//   public static class PBEWithTiger
-//       extends PBEKeyFactory
-//   {
-//       public PBEWithTiger()
-//       {
-//           super("PBEwithHmacTiger", null, false, PKCS12, TIGER, 192, 0);
-//       }
-//   }
-// END android-removed
-   
-   /**
-    * PBEWithSHA1And128BitAES-BC
-    */
-   static public class PBEWithSHAAnd128BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHAAnd128BitAESBC()
-       {
-           super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
-       }
-   }
-   
-   /**
-    * PBEWithSHA1And192BitAES-BC
-    */
-   static public class PBEWithSHAAnd192BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHAAnd192BitAESBC()
-       {
-           super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
-       }
-   }
-   
-   /**
-    * PBEWithSHA1And256BitAES-BC
-    */
-   static public class PBEWithSHAAnd256BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHAAnd256BitAESBC()
-       {
-           super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
-       }
-   }
-   
-   /**
-    * PBEWithSHA256And128BitAES-BC
-    */
-   static public class PBEWithSHA256And128BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA256And128BitAESBC()
-       {
-           super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
-       }
-   }
-   
-   /**
-    * PBEWithSHA256And192BitAES-BC
-    */
-   static public class PBEWithSHA256And192BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA256And192BitAESBC()
-       {
-           super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
-       }
-   }
-   
-   /**
-    * PBEWithSHA256And256BitAES-BC
-    */
-   static public class PBEWithSHA256And256BitAESBC
-       extends PBEKeyFactory
-   {
-       public PBEWithSHA256And256BitAESBC()
-       {
-           super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
-       }
-   }
+   // BEGIN android-removed
+   // /**
+   //  * PBEWithSHAAnd2-KeyTripleDES-CBC
+   //  */
+   // static public class PBEWithSHAAndDES2Key
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAndDES2Key()
+   //     {
+   //         super("PBEwithSHAandDES2Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, true, PKCS12, SHA1, 128, 64);
+   //     }
+   // }
+   // /**
+   //  * PBEWithSHAAnd128BitRC2-CBC
+   //  */
+   // static public class PBEWithSHAAnd128BitRC2
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd128BitRC2()
+   //     {
+   //         super("PBEwithSHAand128BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, true, PKCS12, SHA1, 128, 64);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHAAnd40BitRC2-CBC
+   //  */
+   // static public class PBEWithSHAAnd40BitRC2
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd40BitRC2()
+   //     {
+   //         super("PBEwithSHAand40BitRC2-CBC", PKCSObjectIdentifiers.pbewithSHAAnd40BitRC2_CBC, true, PKCS12, SHA1, 40, 64);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHAAndTwofish-CBC
+   //  */
+   // static public class PBEWithSHAAndTwofish
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAndTwofish()
+   //     {
+   //         super("PBEwithSHAandTwofish-CBC", null, true, PKCS12, SHA1, 256, 128);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHAAnd128BitRC4
+   //  */
+   // static public class PBEWithSHAAnd128BitRC4
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd128BitRC4()
+   //     {
+   //         super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 128, 0);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHAAnd40BitRC4
+   //  */
+   // static public class PBEWithSHAAnd40BitRC4
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd40BitRC4()
+   //     {
+   //         super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 40, 0);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithHmacRIPEMD160
+   //  */
+   // public static class PBEWithRIPEMD160
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithRIPEMD160()
+   //     {
+   //         super("PBEwithHmacRIPEMD160", null, false, PKCS12, RIPEMD160, 160, 0);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithHmacSHA
+   //  */
+   // public static class PBEWithSHA
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA()
+   //     {
+   //         super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithHmacTiger
+   //  */
+   // public static class PBEWithTiger
+   //    extends PBEKeyFactory
+   // {
+   //     public PBEWithTiger()
+   //     {
+   //         super("PBEwithHmacTiger", null, false, PKCS12, TIGER, 192, 0);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA1And128BitAES-BC
+   //  */
+   // static public class PBEWithSHAAnd128BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd128BitAESBC()
+   //     {
+   //         super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA1And192BitAES-BC
+   //  */
+   // static public class PBEWithSHAAnd192BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd192BitAESBC()
+   //     {
+   //         super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA1And256BitAES-BC
+   //  */
+   // static public class PBEWithSHAAnd256BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHAAnd256BitAESBC()
+   //     {
+   //         super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA256And128BitAES-BC
+   //  */
+   // static public class PBEWithSHA256And128BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA256And128BitAESBC()
+   //     {
+   //         super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
+   //     }
+   // }
+   //   
+   // /**
+   //  * PBEWithSHA256And192BitAES-BC
+   //  */
+   // static public class PBEWithSHA256And192BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA256And192BitAESBC()
+   //     {
+   //         super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
+   //     }
+   // }
+   //
+   // /**
+   //  * PBEWithSHA256And256BitAES-BC
+   //  */
+   // static public class PBEWithSHA256And256BitAESBC
+   //     extends PBEKeyFactory
+   // {
+   //     public PBEWithSHA256And256BitAESBC()
+   //     {
+   //         super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
+   //     }
+   // }
+   // END android-removed
    
    /**
     * PBEWithMD5And128BitAES-OpenSSL
diff --git a/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java b/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
deleted file mode 100644
index fe1a2be..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
+++ /dev/null
@@ -1,559 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.Cipher;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-import javax.crypto.spec.RC5ParameterSpec;
-
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.StreamBlockCipher;
-import org.bouncycastle.crypto.StreamCipher;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.engines.BlowfishEngine;
-// END android-removed
-import org.bouncycastle.crypto.engines.DESEngine;
-import org.bouncycastle.crypto.engines.DESedeEngine;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.engines.IDEAEngine;
-// import org.bouncycastle.crypto.engines.RC4Engine;
-// import org.bouncycastle.crypto.engines.SkipjackEngine;
-// import org.bouncycastle.crypto.engines.TwofishEngine;
-// END android-removed
-import org.bouncycastle.crypto.modes.CFBBlockCipher;
-import org.bouncycastle.crypto.modes.OFBBlockCipher;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-
-public class JCEStreamCipher
-    extends WrapCipherSpi implements PBE
-{
-    //
-    // specs we can handle.
-    //
-    private Class[]                 availableSpecs =
-                                    {
-                                        RC2ParameterSpec.class,
-                                        RC5ParameterSpec.class,
-                                        IvParameterSpec.class,
-                                        PBEParameterSpec.class
-                                    };
-
-    private StreamCipher       cipher;
-    private ParametersWithIV   ivParam;
-
-    private int                     ivLength = 0;
-
-    private PBEParameterSpec        pbeSpec = null;
-    private String                  pbeAlgorithm = null;
-
-    protected JCEStreamCipher(
-        StreamCipher engine)
-    {
-        cipher = engine;
-    }
-        
-    protected JCEStreamCipher(
-        BlockCipher engine,
-        int         ivLength)
-    {
-        this.ivLength = ivLength;
-
-        cipher = new StreamBlockCipher(engine);
-    }
-
-    protected int engineGetBlockSize() 
-    {
-        return 0;
-    }
-
-    protected byte[] engineGetIV() 
-    {
-        return (ivParam != null) ? ivParam.getIV() : null;
-    }
-
-    protected int engineGetKeySize(
-        Key     key) 
-    {
-        return key.getEncoded().length * 8;
-    }
-
-    protected int engineGetOutputSize(
-        int     inputLen) 
-    {
-        return inputLen;
-    }
-
-    protected AlgorithmParameters engineGetParameters() 
-    {
-        if (engineParams == null)
-        {
-            if (pbeSpec != null)
-            {
-                try
-                {
-                    AlgorithmParameters engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, "BC");
-                    engineParams.init(pbeSpec);
-                    
-                    return engineParams;
-                }
-                catch (Exception e)
-                {
-                    return null;
-                }
-            }
-        }
-        
-        return engineParams;
-    }
-
-    /**
-     * should never be called.
-     */
-    protected void engineSetMode(
-        String  mode) 
-    {
-        if (!mode.equalsIgnoreCase("ECB"))
-        {
-            throw new IllegalArgumentException("can't support mode " + mode);
-        }
-    }
-
-    /**
-     * should never be called.
-     */
-    protected void engineSetPadding(
-        String  padding) 
-    throws NoSuchPaddingException
-    {
-        if (!padding.equalsIgnoreCase("NoPadding"))
-        {
-            throw new NoSuchPaddingException("Padding " + padding + " unknown.");
-        }
-    }
-
-    protected void engineInit(
-        int                     opmode,
-        Key                     key,
-        AlgorithmParameterSpec  params,
-        SecureRandom            random) 
-        throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        CipherParameters        param;
-
-        this.pbeSpec = null;
-        this.pbeAlgorithm = null;
-        
-        this.engineParams = null;
-        
-        //
-        // basic key check
-        //
-        if (!(key instanceof SecretKey))
-        {
-            throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
-        }
-        
-        if (key instanceof JCEPBEKey)
-        {
-            JCEPBEKey   k = (JCEPBEKey)key;
-            
-            if (k.getOID() != null)
-            {
-                pbeAlgorithm = k.getOID().getId();
-            }
-            else
-            {
-                pbeAlgorithm = k.getAlgorithm();
-            }
-            
-            if (k.getParam() != null)
-            {
-                param = k.getParam();                
-                pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount());
-            }
-            else if (params instanceof PBEParameterSpec)
-            {
-                param = PBE.Util.makePBEParameters(k, params, cipher.getAlgorithmName());
-                pbeSpec = (PBEParameterSpec)params;
-            }
-            else
-            {
-                throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
-            }
-            
-            if (k.getIvSize() != 0)
-            {
-                ivParam = (ParametersWithIV)param;
-            }
-        }
-        else if (params == null)
-        {
-            param = new KeyParameter(key.getEncoded());
-        }
-        else if (params instanceof IvParameterSpec)
-        {
-            param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
-            ivParam = (ParametersWithIV)param;
-        }
-        else
-        {
-            throw new IllegalArgumentException("unknown parameter type.");
-        }
-
-        if ((ivLength != 0) && !(param instanceof ParametersWithIV))
-        {
-            SecureRandom    ivRandom = random;
-
-            if (ivRandom == null)
-            {
-                ivRandom = new SecureRandom();
-            }
-
-            if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
-            {
-                byte[]  iv = new byte[ivLength];
-
-                ivRandom.nextBytes(iv);
-                param = new ParametersWithIV(param, iv);
-                ivParam = (ParametersWithIV)param;
-            }
-            else
-            {
-                throw new InvalidAlgorithmParameterException("no IV set when one expected");
-            }
-        }
-
-        switch (opmode)
-        {
-        case Cipher.ENCRYPT_MODE:
-        case Cipher.WRAP_MODE:
-            cipher.init(true, param);
-            break;
-        case Cipher.DECRYPT_MODE:
-        case Cipher.UNWRAP_MODE:
-            cipher.init(false, param);
-            break;
-        default:
-            System.out.println("eeek!");
-        }
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        AlgorithmParameters params,
-        SecureRandom        random) 
-        throws InvalidKeyException, InvalidAlgorithmParameterException
-    {
-        AlgorithmParameterSpec  paramSpec = null;
-
-        if (params != null)
-        {
-            for (int i = 0; i != availableSpecs.length; i++)
-            {
-                try
-                {
-                    paramSpec = params.getParameterSpec(availableSpecs[i]);
-                    break;
-                }
-                catch (Exception e)
-                {
-                    continue;
-                }
-            }
-
-            if (paramSpec == null)
-            {
-                throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
-            }
-        }
-
-        engineInit(opmode, key, paramSpec, random);
-        engineParams = params;
-    }
-
-    protected void engineInit(
-        int                 opmode,
-        Key                 key,
-        SecureRandom        random) 
-        throws InvalidKeyException
-    {
-        try
-        {
-            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
-        }
-        catch (InvalidAlgorithmParameterException e)
-        {
-            throw new InvalidKeyException(e.getMessage());
-        }
-    }
-
-    protected byte[] engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-    {
-        byte[]  out = new byte[inputLen];
-
-        cipher.processBytes(input, inputOffset, inputLen, out, 0);
-
-        return out;
-    }
-
-    protected int engineUpdate(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-        throws ShortBufferException 
-    {
-        try
-        {
-        cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
-
-        return inputLen;
-        }
-        catch (DataLengthException e)
-        {
-            throw new ShortBufferException(e.getMessage());
-        }
-    }
-
-    protected byte[] engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen) 
-    {
-        if (inputLen != 0)
-        {
-            byte[] out = engineUpdate(input, inputOffset, inputLen);
-
-            cipher.reset();
-            
-            return out;
-        }
-
-        cipher.reset();
-        
-        return new byte[0];
-    }
-
-    protected int engineDoFinal(
-        byte[]  input,
-        int     inputOffset,
-        int     inputLen,
-        byte[]  output,
-        int     outputOffset) 
-    {
-        if (inputLen != 0)
-        {
-            cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
-        }
-
-        cipher.reset();
-        
-        return inputLen;
-    }
-
-    /*
-     * The ciphers that inherit from us.
-     */
-
-    /**
-     * DES
-     */
-    static public class DES_CFB8
-        extends JCEStreamCipher
-    {
-        public DES_CFB8()
-        {
-            super(new CFBBlockCipher(new DESEngine(), 8), 64);
-        }
-    }
-
-    /**
-     * DESede
-     */
-    static public class DESede_CFB8
-        extends JCEStreamCipher
-    {
-        public DESede_CFB8()
-        {
-            super(new CFBBlockCipher(new DESedeEngine(), 8), 64);
-        }
-    }
-
-// BEGIN android-removed
-//    /**
-//     * SKIPJACK
-//     */
-//    static public class Skipjack_CFB8
-//        extends JCEStreamCipher
-//    {
-//        public Skipjack_CFB8()
-//        {
-//            super(new CFBBlockCipher(new SkipjackEngine(), 8), 64);
-//        }
-//    }
-//
-//    /**
-//     * Blowfish
-//     */
-//    static public class Blowfish_CFB8
-//        extends JCEStreamCipher
-//    {
-//        public Blowfish_CFB8()
-//        {
-//            super(new CFBBlockCipher(new BlowfishEngine(), 8), 64);
-//        }
-//    }
-//
-//    /**
-//     * Twofish
-//     */
-//    static public class Twofish_CFB8
-//        extends JCEStreamCipher
-//    {
-//        public Twofish_CFB8()
-//        {
-//            super(new CFBBlockCipher(new TwofishEngine(), 8), 128);
-//        }
-//    }
-//
-//    /**
-//     * IDEA
-//     */
-//    static public class IDEA_CFB8
-//        extends JCEStreamCipher
-//    {
-//        public IDEA_CFB8()
-//        {
-//            super(new CFBBlockCipher(new IDEAEngine(), 8), 64);
-//        }
-//    }
-// END android-removed
-
-    /**
-     * DES
-     */
-    static public class DES_OFB8
-        extends JCEStreamCipher
-    {
-        public DES_OFB8()
-        {
-            super(new OFBBlockCipher(new DESEngine(), 8), 64);
-        }
-    }
-
-    /**
-     * DESede
-     */
-    static public class DESede_OFB8
-        extends JCEStreamCipher
-    {
-        public DESede_OFB8()
-        {
-            super(new OFBBlockCipher(new DESedeEngine(), 8), 64);
-        }
-    }
-
-//  BEGIN android-removed
-//    /**
-//     * SKIPJACK
-//     */
-//    static public class Skipjack_OFB8
-//        extends JCEStreamCipher
-//    {
-//        public Skipjack_OFB8()
-//        {
-//            super(new OFBBlockCipher(new SkipjackEngine(), 8), 64);
-//        }
-//    }
-//
-//    /**
-//     * Blowfish
-//     */
-//    static public class Blowfish_OFB8
-//        extends JCEStreamCipher
-//    {
-//        public Blowfish_OFB8()
-//        {
-//            super(new OFBBlockCipher(new BlowfishEngine(), 8), 64);
-//        }
-//    }
-//
-//    /**
-//     * Twofish
-//     */
-//    static public class Twofish_OFB8
-//        extends JCEStreamCipher
-//    {
-//        public Twofish_OFB8()
-//        {
-//            super(new OFBBlockCipher(new TwofishEngine(), 8), 128);
-//        }
-//    }
-//
-//    /**
-//     * IDEA
-//     */
-//    static public class IDEA_OFB8
-//        extends JCEStreamCipher
-//    {
-//        public IDEA_OFB8()
-//        {
-//            super(new OFBBlockCipher(new IDEAEngine(), 8), 64);
-//        }
-//    }
-//
-//    /**
-//     * RC4
-//     */
-//    static public class RC4
-//        extends JCEStreamCipher
-//    {
-//        public RC4()
-//        {
-//            super(new RC4Engine());
-//        }
-//    }
-//
-//    /**
-//     * PBEWithSHAAnd128BitRC4
-//     */
-//    static public class PBEWithSHAAnd128BitRC4
-//        extends JCEStreamCipher
-//    {
-//        public PBEWithSHAAnd128BitRC4()
-//        {
-//            super(new RC4Engine());
-//        }
-//    }
-//
-//    /**
-//     * PBEWithSHAAnd40BitRC4
-//     */
-//    static public class PBEWithSHAAnd40BitRC4
-//        extends JCEStreamCipher
-//    {
-//        public PBEWithSHAAnd40BitRC4()
-//        {
-//            super(new RC4Engine());
-//        }
-//    }
-// END android-removed
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java b/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java
index a72cb13..f1865ec 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameterGenerator.java
@@ -1,17 +1,5 @@
 package org.bouncycastle.jce.provider;
 
-import java.security.AlgorithmParameterGeneratorSpi;
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.DSAParameterSpec;
-
-import javax.crypto.spec.DHGenParameterSpec;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-
 import org.bouncycastle.crypto.generators.DHParametersGenerator;
 import org.bouncycastle.crypto.generators.DSAParametersGenerator;
 // BEGIN android-removed
@@ -27,6 +15,18 @@
 // import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
 // END android-removed
 
+import javax.crypto.spec.DHGenParameterSpec;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+import java.security.AlgorithmParameterGeneratorSpi;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+
 public abstract class JDKAlgorithmParameterGenerator
     extends AlgorithmParameterGeneratorSpi
 {
@@ -97,6 +97,19 @@
         extends JDKAlgorithmParameterGenerator
     {
         protected void engineInit(
+            int             strength,
+            SecureRandom    random)
+        {
+            if (strength < 512 || strength > 1024 || strength % 64 != 0)
+            {
+                throw new InvalidParameterException("strength must be from 512 - 1024 and a multiple of 64");
+            }
+
+            this.strength = strength;
+            this.random = random;
+        }
+
+        protected void engineInit(
             AlgorithmParameterSpec  genParamSpec,
             SecureRandom            random)
             throws InvalidAlgorithmParameterException
@@ -135,312 +148,198 @@
         }
     }
 
-// BEGIN android-removed
-//    public static class GOST3410
-//        extends JDKAlgorithmParameterGenerator
-//    {
-//        protected void engineInit(
-//                AlgorithmParameterSpec  genParamSpec,
-//                SecureRandom            random)
-//        throws InvalidAlgorithmParameterException
-//        {
-//            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST3410 parameter generation.");
-//        }
-//        
-//        protected AlgorithmParameters engineGenerateParameters()
-//        {
-//            GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
-//            
-//            if (random != null)
-//            {
-//                pGen.init(strength, 2, random);
-//            }
-//            else
-//            {
-//                pGen.init(strength, 2, new SecureRandom());
-//            }
-//            
-//            GOST3410Parameters p = pGen.generateParameters();
-//            
-//            AlgorithmParameters params;
-//            
-//            try
-//            {
-//                params = AlgorithmParameters.getInstance("GOST3410", "BC");
-//                params.init(new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(p.getP(), p.getQ(), p.getA())));
-//            }
-//            catch (Exception e)
-//            {
-//                throw new RuntimeException(e.getMessage());
-//            }
-//            
-//            return params;
-//        }
-//    }
-//    
-//    public static class ElGamal
-//        extends JDKAlgorithmParameterGenerator
-//    {
-//        private int l = 0;
-//        
-//        protected void engineInit(
-//            AlgorithmParameterSpec  genParamSpec,
-//            SecureRandom            random)
-//            throws InvalidAlgorithmParameterException
-//        {
-//            if (!(genParamSpec instanceof DHGenParameterSpec))
-//            {
-//                throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation");
-//            }
-//            DHGenParameterSpec  spec = (DHGenParameterSpec)genParamSpec;
-//
-//            this.strength = spec.getPrimeSize();
-//            this.l = spec.getExponentSize();
-//            this.random = random;
-//        }
-//
-//        protected AlgorithmParameters engineGenerateParameters()
-//        {
-//            ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
-//
-//            if (random != null)
-//            {
-//                pGen.init(strength, 20, random);
-//            }
-//            else
-//            {
-//                pGen.init(strength, 20, new SecureRandom());
-//            }
-//
-//            ElGamalParameters p = pGen.generateParameters();
-//
-//            AlgorithmParameters params;
-//
-//            try
-//            {
-//                params = AlgorithmParameters.getInstance("ElGamal", "BC");
-//                params.init(new DHParameterSpec(p.getP(), p.getG(), l));
-//            }
-//            catch (Exception e)
-//            {
-//                throw new RuntimeException(e.getMessage());
-//            }
-//
-//            return params;
-//        }
-//    }
-// END android-removed
-
-    public static class DES
-        extends JDKAlgorithmParameterGenerator
-    {
-        protected void engineInit(
-            AlgorithmParameterSpec  genParamSpec,
-            SecureRandom            random)
-            throws InvalidAlgorithmParameterException
-        {
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DES parameter generation.");
-        }
-
-        protected AlgorithmParameters engineGenerateParameters()
-        {
-            byte[]  iv = new byte[8];
-
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            random.nextBytes(iv);
-
-            AlgorithmParameters params;
-
-            try
-            {
-                params = AlgorithmParameters.getInstance("DES", "BC");
-                params.init(new IvParameterSpec(iv));
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException(e.getMessage());
-            }
-
-            return params;
-        }
-    }
-
-    public static class RC2
-        extends JDKAlgorithmParameterGenerator
-    {
-        RC2ParameterSpec    spec = null;
-
-        protected void engineInit(
-            AlgorithmParameterSpec  genParamSpec,
-            SecureRandom            random)
-            throws InvalidAlgorithmParameterException
-        {
-            if (genParamSpec instanceof RC2ParameterSpec)
-            {
-                spec = (RC2ParameterSpec)genParamSpec;
-                return;
-            }
-
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for RC2 parameter generation.");
-        }
-
-        protected AlgorithmParameters engineGenerateParameters()
-        {
-            AlgorithmParameters params;
-
-            if (spec == null)
-            {
-                byte[]  iv = new byte[8];
-
-                if (random == null)
-                {
-                    random = new SecureRandom();
-                }
-
-                random.nextBytes(iv);
-
-                try
-                {
-                    params = AlgorithmParameters.getInstance("RC2", "BC");
-                    params.init(new IvParameterSpec(iv));
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException(e.getMessage());
-                }
-            }
-            else
-            {
-                try
-                {
-                    params = AlgorithmParameters.getInstance("RC2", "BC");
-                    params.init(spec);
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException(e.getMessage());
-                }
-            }
-
-            return params;
-        }
-    }
-
-    public static class AES
-        extends JDKAlgorithmParameterGenerator
-    {
-        protected void engineInit(
-            AlgorithmParameterSpec  genParamSpec,
-            SecureRandom            random)
-            throws InvalidAlgorithmParameterException
-        {
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for AES parameter generation.");
-        }
-
-        protected AlgorithmParameters engineGenerateParameters()
-        {
-            byte[]  iv = new byte[16];
-
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            random.nextBytes(iv);
-
-            AlgorithmParameters params;
-
-            try
-            {
-                params = AlgorithmParameters.getInstance("AES", "BC");
-                params.init(new IvParameterSpec(iv));
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException(e.getMessage());
-            }
-
-            return params;
-        }
-    }
-
-    public static class IDEA
-        extends JDKAlgorithmParameterGenerator
-    {
-        protected void engineInit(
-            AlgorithmParameterSpec  genParamSpec,
-            SecureRandom            random)
-            throws InvalidAlgorithmParameterException
-        {
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for IDEA parameter generation.");
-        }
-
-        protected AlgorithmParameters engineGenerateParameters()
-        {
-            byte[]  iv = new byte[8];
-
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            random.nextBytes(iv);
-
-            AlgorithmParameters params;
-
-            try
-            {
-                params = AlgorithmParameters.getInstance("IDEA", "BC");
-                params.init(new IvParameterSpec(iv));
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException(e.getMessage());
-            }
-
-            return params;
-        }
-    }
-
-    public static class CAST5
-        extends JDKAlgorithmParameterGenerator
-    {
-        protected void engineInit(
-            AlgorithmParameterSpec  genParamSpec,
-            SecureRandom            random)
-            throws InvalidAlgorithmParameterException
-        {
-            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for CAST5 parameter generation.");
-        }
-
-        protected AlgorithmParameters engineGenerateParameters()
-        {
-            byte[]  iv = new byte[8];
-
-            if (random == null)
-            {
-                random = new SecureRandom();
-            }
-
-            random.nextBytes(iv);
-
-            AlgorithmParameters params;
-
-            try
-            {
-                params = AlgorithmParameters.getInstance("CAST5", "BC");
-                params.init(new IvParameterSpec(iv));
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException(e.getMessage());
-            }
-
-            return params;
-        }
-    }
+   // BEGIN android-removed
+   // public static class GOST3410
+   //     extends JDKAlgorithmParameterGenerator
+   // {
+   //     protected void engineInit(
+   //             AlgorithmParameterSpec  genParamSpec,
+   //             SecureRandom            random)
+   //     throws InvalidAlgorithmParameterException
+   //     {
+   //         throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for GOST3410 parameter generation.");
+   //     }
+   //       
+   //     protected AlgorithmParameters engineGenerateParameters()
+   //     {
+   //         GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+   //           
+   //         if (random != null)
+   //         {
+   //             pGen.init(strength, 2, random);
+   //         }
+   //         else
+   //         {
+   //             pGen.init(strength, 2, new SecureRandom());
+   //         }
+   //           
+   //         GOST3410Parameters p = pGen.generateParameters();
+   //           
+   //         AlgorithmParameters params;
+   //           
+   //         try
+   //         {
+   //             params = AlgorithmParameters.getInstance("GOST3410", "BC");
+   //             params.init(new GOST3410ParameterSpec(new GOST3410PublicKeyParameterSetSpec(p.getP(), p.getQ(), p.getA())));
+   //         }
+   //         catch (Exception e)
+   //         {
+   //             throw new RuntimeException(e.getMessage());
+   //         }
+   //           
+   //         return params;
+   //     }
+   // }
+   //   
+   // public static class ElGamal
+   //     extends JDKAlgorithmParameterGenerator
+   // {
+   //     private int l = 0;
+   //       
+   //     protected void engineInit(
+   //         AlgorithmParameterSpec  genParamSpec,
+   //         SecureRandom            random)
+   //         throws InvalidAlgorithmParameterException
+   //     {
+   //         if (!(genParamSpec instanceof DHGenParameterSpec))
+   //         {
+   //             throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation");
+   //         }
+   //         DHGenParameterSpec  spec = (DHGenParameterSpec)genParamSpec;
+   //
+   //         this.strength = spec.getPrimeSize();
+   //         this.l = spec.getExponentSize();
+   //         this.random = random;
+   //     }
+   //
+   //     protected AlgorithmParameters engineGenerateParameters()
+   //     {
+   //         ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
+   //
+   //         if (random != null)
+   //         {
+   //             pGen.init(strength, 20, random);
+   //         }
+   //         else
+   //         {
+   //             pGen.init(strength, 20, new SecureRandom());
+   //         }
+   //
+   //         ElGamalParameters p = pGen.generateParameters();
+   //
+   //         AlgorithmParameters params;
+   //
+   //         try
+   //         {
+   //             params = AlgorithmParameters.getInstance("ElGamal", "BC");
+   //             params.init(new DHParameterSpec(p.getP(), p.getG(), l));
+   //         }
+   //         catch (Exception e)
+   //         {
+   //             throw new RuntimeException(e.getMessage());
+   //         }
+   //
+   //         return params;
+   //     }
+   // }
+   //
+   //  public static class DES
+   //      extends JDKAlgorithmParameterGenerator
+   //  {
+   //      protected void engineInit(
+   //          AlgorithmParameterSpec  genParamSpec,
+   //          SecureRandom            random)
+   //          throws InvalidAlgorithmParameterException
+   //      {
+   //          throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for DES parameter generation.");
+   //      }
+   //
+   //      protected AlgorithmParameters engineGenerateParameters()
+   //      {
+   //          byte[]  iv = new byte[8];
+   //
+   //          if (random == null)
+   //          {
+   //              random = new SecureRandom();
+   //          }
+   //
+   //          random.nextBytes(iv);
+   //
+   //          AlgorithmParameters params;
+   //
+   //          try
+   //          {
+   //              params = AlgorithmParameters.getInstance("DES", "BC");
+   //              params.init(new IvParameterSpec(iv));
+   //          }
+   //          catch (Exception e)
+   //          {
+   //              throw new RuntimeException(e.getMessage());
+   //          }
+   //
+   //          return params;
+   //      }
+   //  }
+   //
+   //  public static class RC2
+   //      extends JDKAlgorithmParameterGenerator
+   //  {
+   //      RC2ParameterSpec    spec = null;
+   //
+   //      protected void engineInit(
+   //          AlgorithmParameterSpec  genParamSpec,
+   //          SecureRandom            random)
+   //          throws InvalidAlgorithmParameterException
+   //      {
+   //          if (genParamSpec instanceof RC2ParameterSpec)
+   //          {
+   //              spec = (RC2ParameterSpec)genParamSpec;
+   //              return;
+   //          }
+   //
+   //          throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for RC2 parameter generation.");
+   //      }
+   //
+   //      protected AlgorithmParameters engineGenerateParameters()
+   //      {
+   //          AlgorithmParameters params;
+   //
+   //          if (spec == null)
+   //          {
+   //              byte[]  iv = new byte[8];
+   //
+   //              if (random == null)
+   //              {
+   //                  random = new SecureRandom();
+   //              }
+   //
+   //              random.nextBytes(iv);
+   //
+   //              try
+   //              {
+   //                  params = AlgorithmParameters.getInstance("RC2", "BC");
+   //                  params.init(new IvParameterSpec(iv));
+   //              }
+   //              catch (Exception e)
+   //              {
+   //                  throw new RuntimeException(e.getMessage());
+   //              }
+   //          }
+   //          else
+   //          {
+   //              try
+   //              {
+   //                  params = AlgorithmParameters.getInstance("RC2", "BC");
+   //                  params.init(spec);
+   //              }
+   //              catch (Exception e)
+   //              {
+   //                  throw new RuntimeException(e.getMessage());
+   //              }
+   //          }
+   //
+   //          return params;
+   //      }
+   //  }
+   // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java b/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
index e3f476d..f71bfcb 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
@@ -1,6 +1,48 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+// import org.bouncycastle.asn1.oiw.ElGamalParameter;
+// END android-removed
+import org.bouncycastle.asn1.pkcs.DHParameter;
+import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+// END android-removed
+import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.pkcs.PBKDF2Params;
+// END android-removed
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
+// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
+// import org.bouncycastle.jce.spec.IESParameterSpec;
+// END android-removed
+import org.bouncycastle.util.Arrays;
+
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.PSource;
+// BEGIN android-removed
+// import javax.crypto.spec.RC2ParameterSpec;
+// END android-removed
 import java.io.IOException;
 import java.security.AlgorithmParametersSpi;
 import java.security.spec.AlgorithmParameterSpec;
@@ -9,85 +51,58 @@
 import java.security.spec.MGF1ParameterSpec;
 import java.security.spec.PSSParameterSpec;
 
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.OAEPParameterSpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.PSource;
-import javax.crypto.spec.RC2ParameterSpec;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-// BEGIN android-removed
-// import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
-// import org.bouncycastle.asn1.misc.CAST5CBCParameters;
-// END android-removed
-import org.bouncycastle.asn1.misc.IDEACBCPar;
-// BEGIN android-removed
-// import org.bouncycastle.asn1.oiw.ElGamalParameter;
-// END android-removed
-import org.bouncycastle.asn1.pkcs.DHParameter;
-import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
-import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
-import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.DSAParameter;
-// BEGIN android-removed
-// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
-// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
-// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
-// END android-removed
-import org.bouncycastle.jce.spec.IESParameterSpec;
-
 public abstract class JDKAlgorithmParameters
     extends AlgorithmParametersSpi
 {
+    protected boolean isASN1FormatString(String format)
+    {
+        return format == null || format.equals("ASN.1");
+    }
+
+    protected AlgorithmParameterSpec engineGetParameterSpec(
+        Class paramSpec)
+        throws InvalidParameterSpecException
+    {
+        if (paramSpec == null)
+        {
+            throw new NullPointerException("argument to getParameterSpec must not be null");
+        }
+
+        return localEngineGetParameterSpec(paramSpec);
+    }
+
+    protected abstract AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec)
+        throws InvalidParameterSpecException;
+
     public static class IVAlgorithmParameters
         extends JDKAlgorithmParameters
     {
         private byte[]  iv;
-
+    
         protected byte[] engineGetEncoded() 
             throws IOException
         {
             return engineGetEncoded("ASN.1");
         }
-
+    
         protected byte[] engineGetEncoded(
             String format) 
             throws IOException
         {
-            if (format == null)
+            if (isASN1FormatString(format))
             {
-                return engineGetEncoded("ASN.1");
+                 return new DEROctetString(engineGetEncoded("RAW")).getEncoded();
             }
-            
+    
             if (format.equals("RAW"))
             {
-                byte[]  tmp = new byte[iv.length];
-
-                System.arraycopy(iv, 0, tmp, 0, iv.length);
-                return tmp;
+                return Arrays.clone(iv);
             }
-            else if (format.equals("ASN.1"))
-            {
-                return new DEROctetString(engineGetEncoded("RAW")).getEncoded();
-            }
-
+    
             return null;
         }
-
-        protected AlgorithmParameterSpec engineGetParameterSpec(
+    
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
             Class paramSpec) 
             throws InvalidParameterSpecException
         {
@@ -95,10 +110,10 @@
             {
                 return new IvParameterSpec(iv);
             }
-
+    
             throw new InvalidParameterSpecException("unknown parameter spec passed to IV parameters object.");
         }
-
+    
         protected void engineInit(
             AlgorithmParameterSpec paramSpec) 
             throws InvalidParameterSpecException
@@ -107,10 +122,10 @@
             {
                 throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object");
             }
-
+    
             this.iv = ((IvParameterSpec)paramSpec).getIV();
         }
-
+    
         protected void engineInit(
             byte[] params) 
             throws IOException
@@ -121,34 +136,24 @@
             if ((params.length % 8) != 0
                     && params[0] == 0x04 && params[1] == params.length - 2)
             {
-                ASN1InputStream         aIn = new ASN1InputStream(params);
-                ASN1OctetString         oct = (ASN1OctetString)aIn.readObject();
-
+                ASN1OctetString oct = (ASN1OctetString)ASN1Object.fromByteArray(params);
+    
                 params = oct.getOctets();
             }
-
-            this.iv = new byte[params.length];
-
-            System.arraycopy(params, 0, iv, 0, iv.length);
+    
+            this.iv = Arrays.clone(params);
         }
-
+    
         protected void engineInit(
             byte[] params,
             String format) 
             throws IOException
         {
-            if (format.equals("RAW"))
+            if (isASN1FormatString(format))
             {
-                engineInit(params);
-                return;
-            }
-            else if (format.equals("ASN.1"))
-            {
-                ASN1InputStream         aIn = new ASN1InputStream(params);
-                
                 try
                 {
-                    ASN1OctetString         oct = (ASN1OctetString)aIn.readObject();
+                    ASN1OctetString oct = (ASN1OctetString)ASN1Object.fromByteArray(params);
     
                     engineInit(oct.getOctets());
                 }
@@ -156,427 +161,311 @@
                 {
                     throw new IOException("Exception decoding: " + e);
                 }
-                
+    
                 return;
             }
-
+    
+            if (format.equals("RAW"))
+            {
+                engineInit(params);
+                return;
+            }
+    
             throw new IOException("Unknown parameters format in IV parameters object");
         }
-
+    
         protected String engineToString() 
         {
             return "IV Parameters";
         }
     }
-
-// BEGIN android-removed
-//    public static class IDEAAlgorithmParameters
-//        extends JDKAlgorithmParameters
-//    {
-//        private byte[]  iv;
-//
-//        protected byte[] engineGetEncoded() 
-//            throws IOException
-//        {
-//            return engineGetEncoded("ASN.1");
-//        }
-//
-//        protected byte[] engineGetEncoded(
-//            String format) 
-//            throws IOException
-//        {
-//            if (format == null)
-//            {
-//                return engineGetEncoded("ASN.1");
-//            }
-//            
-//            if (format.equals("RAW"))
-//            {
-//                byte[]  tmp = new byte[iv.length];
-//
-//                System.arraycopy(iv, 0, tmp, 0, iv.length);
-//                return tmp;
-//            }
-//            else if (format.equals("ASN.1"))
-//            {
-//                return new IDEACBCPar(engineGetEncoded("RAW")).getEncoded();
-//            }
-//
-//            return null;
-//        }
-//
-//        protected AlgorithmParameterSpec engineGetParameterSpec(
-//            Class paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec == IvParameterSpec.class)
-//            {
-//                return new IvParameterSpec(iv);
-//            }
-//
-//            throw new InvalidParameterSpecException("unknown parameter spec passed to IV parameters object.");
-//        }
-//
-//        protected void engineInit(
-//            AlgorithmParameterSpec paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (!(paramSpec instanceof IvParameterSpec))
-//            {
-//                throw new InvalidParameterSpecException("IvParameterSpec required to initialise a IV parameters algorithm parameters object");
-//            }
-//
-//            this.iv = ((IvParameterSpec)paramSpec).getIV();
-//        }
-//
-//        protected void engineInit(
-//            byte[] params) 
-//            throws IOException
-//        {
-//            this.iv = new byte[params.length];
-//
-//            System.arraycopy(params, 0, iv, 0, iv.length);
-//        }
-//
-//        protected void engineInit(
-//            byte[] params,
-//            String format) 
-//            throws IOException
-//        {
-//            if (format.equals("RAW"))
-//            {
-//                engineInit(params);
-//                return;
-//            }
-//            else if (format.equals("ASN.1"))
-//            {
-//                ASN1InputStream         aIn = new ASN1InputStream(params);
-//                IDEACBCPar              oct = new IDEACBCPar((ASN1Sequence)aIn.readObject());
-//
-//                engineInit(oct.getIV());
-//                return;
-//            }
-//
-//            throw new IOException("Unknown parameters format in IV parameters object");
-//        }
-//
-//        protected String engineToString() 
-//        {
-//            return "IDEA Parameters";
-//        }
-//    }
-//
-//    public static class RC2AlgorithmParameters
-//        extends JDKAlgorithmParameters
-//    {
-//        private short[] table = {
-//           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 short[] ekb = {
-//           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
-//        };
-//
-//        private byte[]  iv;
-//        private int     parameterVersion = 58;
-//
-//        protected byte[] engineGetEncoded() 
-//        {
-//            byte[]  tmp = new byte[iv.length];
-//
-//            System.arraycopy(iv, 0, tmp, 0, iv.length);
-//            return tmp;
-//        }
-//
-//        protected byte[] engineGetEncoded(
-//            String format) 
-//            throws IOException
-//        {
-//            if (format.equals("RAW"))
-//            {
-//                return engineGetEncoded();
-//            }
-//            else if (format.equals("ASN.1"))
-//            {
-//                if (parameterVersion == -1)
-//                {
-//                    return new RC2CBCParameter(engineGetEncoded()).getEncoded();
-//                }
-//                else
-//                {
-//                    return new RC2CBCParameter(parameterVersion, engineGetEncoded()).getEncoded();
-//                }
-//            }
-//
-//            return null;
-//        }
-//
-//        protected AlgorithmParameterSpec engineGetParameterSpec(
-//            Class paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec == RC2ParameterSpec.class)
-//            {
-//                if (parameterVersion != -1)
-//                {
-//                    if (parameterVersion < 256)
-//                    {
-//                        return new RC2ParameterSpec(ekb[parameterVersion], iv);
-//                    }
-//                    else
-//                    {
-//                        return new RC2ParameterSpec(parameterVersion, iv);
-//                    }
-//                }
-//            }
-//
-//            if (paramSpec == IvParameterSpec.class)
-//            {
-//                return new IvParameterSpec(iv);
-//            }
-//
-//            throw new InvalidParameterSpecException("unknown parameter spec passed to RC2 parameters object.");
-//        }
-//
-//        protected void engineInit(
-//            AlgorithmParameterSpec paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec instanceof IvParameterSpec)
-//            {
-//                this.iv = ((IvParameterSpec)paramSpec).getIV();
-//            }
-//            else if (paramSpec instanceof RC2ParameterSpec)
-//            {
-//                int effKeyBits = ((RC2ParameterSpec)paramSpec).getEffectiveKeyBits();
-//                if (effKeyBits != -1)
-//                {
-//                    if (effKeyBits < 256)
-//                    {
-//                        parameterVersion = table[effKeyBits];
-//                    }
-//                    else
-//                    {
-//                        parameterVersion = effKeyBits;
-//                    }
-//                }
-//
-//                this.iv = ((RC2ParameterSpec)paramSpec).getIV();
-//            }
-//            else
-//            {
-//                throw new InvalidParameterSpecException("IvParameterSpec or RC2ParameterSpec required to initialise a RC2 parameters algorithm parameters object");
-//            }
-//        }
-//
-//        protected void engineInit(
-//            byte[] params) 
-//            throws IOException
-//        {
-//            this.iv = new byte[params.length];
-//
-//            System.arraycopy(params, 0, iv, 0, iv.length);
-//        }
-//
-//        protected void engineInit(
-//            byte[] params,
-//            String format) 
-//            throws IOException
-//        {
-//            if (format.equals("RAW"))
-//            {
-//                engineInit(params);
-//                return;
-//            }
-//            else if (format.equals("ASN.1"))
-//            {
-//                ASN1InputStream         aIn = new ASN1InputStream(params);
-//                RC2CBCParameter         p = RC2CBCParameter.getInstance(aIn.readObject());
-//
-//                if (p.getRC2ParameterVersion() != null)
-//                {
-//                    parameterVersion = p.getRC2ParameterVersion().intValue();
-//                }
-//
-//                iv = p.getIV();
-//
-//                return;
-//            }
-//
-//            throw new IOException("Unknown parameters format in IV parameters object");
-//        }
-//
-//        protected String engineToString() 
-//        {
-//            return "RC2 Parameters";
-//        }
-//    }
-//
-//    public static class CAST5AlgorithmParameters
-//        extends JDKAlgorithmParameters
-//    {
-//        private byte[]  iv;
-//        private int     keyLength = 128;
-//
-//        protected byte[] engineGetEncoded() 
-//        {
-//            byte[]  tmp = new byte[iv.length];
-//
-//            System.arraycopy(iv, 0, tmp, 0, iv.length);
-//            return tmp;
-//        }
-//
-//        protected byte[] engineGetEncoded(
-//            String format) 
-//            throws IOException 
-//        {
-//            if (format.equals("RAW"))
-//            {
-//                return engineGetEncoded();
-//            }
-//            // BEGIN android-removed
-//            // else if (format.equals("ASN.1"))
-//            // {
-//            //     return new CAST5CBCParameters(engineGetEncoded(), keyLength).getEncoded();
-//            // }
-//            // END android-removed
-//
-//            return null;
-//        }
-//
-//        protected AlgorithmParameterSpec engineGetParameterSpec(
-//            Class paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec == IvParameterSpec.class)
-//            {
-//                return new IvParameterSpec(iv);
-//            }
-//
-//            throw new InvalidParameterSpecException("unknown parameter spec passed to CAST5 parameters object.");
-//        }
-//
-//        protected void engineInit(
-//            AlgorithmParameterSpec paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec instanceof IvParameterSpec)
-//            {
-//                this.iv = ((IvParameterSpec)paramSpec).getIV();
-//            }
-//            else
-//            {
-//                throw new InvalidParameterSpecException("IvParameterSpec required to initialise a CAST5 parameters algorithm parameters object");
-//            }
-//        }
-//
-//        protected void engineInit(
-//            byte[] params) 
-//            throws IOException
-//        {
-//            this.iv = new byte[params.length];
-//
-//            System.arraycopy(params, 0, iv, 0, iv.length);
-//        }
-//
-//        protected void engineInit(
-//            byte[] params,
-//            String format) 
-//            throws IOException
-//        {
-//            if (format.equals("RAW"))
-//            {
-//                engineInit(params);
-//                return;
-//            }
-//            // BEGIN android-removed
-//            // else if (format.equals("ASN.1"))
-//            // {
-//            //     ASN1InputStream         aIn = new ASN1InputStream(params);
-//            //     CAST5CBCParameters      p = CAST5CBCParameters.getInstance(aIn.readObject());
-//            //
-//            //     keyLength = p.getKeyLength();
-//            //
-//            //     iv = p.getIV();
-//            //
-//            //     return;
-//            // }
-//            // END android-removed
-//
-//            throw new IOException("Unknown parameters format in IV parameters object");
-//        }
-//
-//        protected String engineToString() 
-//        {
-//            return "CAST5 Parameters";
-//        }
-//    }
-// END android-removed
-
+    
+    // BEGIN android-removed
+    // public static class RC2AlgorithmParameters
+    //     extends JDKAlgorithmParameters
+    // {
+    //     private static final short[] table = {
+    //        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[] ekb = {
+    //        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
+    //     };
+    //
+    //     private byte[]  iv;
+    //     private int     parameterVersion = 58;
+    //
+    //     protected byte[] engineGetEncoded() 
+    //     {
+    //         return Arrays.clone(iv);
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format))
+    //         {
+    //             if (parameterVersion == -1)
+    //             {
+    //                 return new RC2CBCParameter(engineGetEncoded()).getEncoded();
+    //             }
+    //             else
+    //             {
+    //                 return new RC2CBCParameter(parameterVersion, engineGetEncoded()).getEncoded();
+    //             }
+    //         }
+    //
+    //         if (format.equals("RAW"))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //         Class paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == RC2ParameterSpec.class)
+    //         {
+    //             if (parameterVersion != -1)
+    //             {
+    //                 if (parameterVersion < 256)
+    //                 {
+    //                     return new RC2ParameterSpec(ekb[parameterVersion], iv);
+    //                 }
+    //                 else
+    //                 {
+    //                     return new RC2ParameterSpec(parameterVersion, iv);
+    //                 }
+    //             }
+    //         }
+    //
+    //         if (paramSpec == IvParameterSpec.class)
+    //         {
+    //             return new IvParameterSpec(iv);
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to RC2 parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec instanceof IvParameterSpec)
+    //         {
+    //             this.iv = ((IvParameterSpec)paramSpec).getIV();
+    //         }
+    //         else if (paramSpec instanceof RC2ParameterSpec)
+    //         {
+    //             int effKeyBits = ((RC2ParameterSpec)paramSpec).getEffectiveKeyBits();
+    //             if (effKeyBits != -1)
+    //             {
+    //                 if (effKeyBits < 256)
+    //                 {
+    //                     parameterVersion = table[effKeyBits];
+    //                 }
+    //                 else
+    //                 {
+    //                     parameterVersion = effKeyBits;
+    //                 }
+    //             }
+    //
+    //             this.iv = ((RC2ParameterSpec)paramSpec).getIV();
+    //         }
+    //         else
+    //         {
+    //             throw new InvalidParameterSpecException("IvParameterSpec or RC2ParameterSpec required to initialise a RC2 parameters algorithm parameters object");
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params) 
+    //         throws IOException
+    //     {
+    //         this.iv = Arrays.clone(params);
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params,
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format))
+    //         {
+    //             RC2CBCParameter p = RC2CBCParameter.getInstance(ASN1Object.fromByteArray(params));
+    //
+    //             if (p.getRC2ParameterVersion() != null)
+    //             {
+    //                 parameterVersion = p.getRC2ParameterVersion().intValue();
+    //             }
+    //
+    //             iv = p.getIV();
+    //
+    //             return;
+    //         }
+    //
+    //         if (format.equals("RAW"))
+    //         {
+    //             engineInit(params);
+    //             return;
+    //         }
+    //
+    //         throw new IOException("Unknown parameters format in IV parameters object");
+    //     }
+    //
+    //     protected String engineToString() 
+    //     {
+    //         return "RC2 Parameters";
+    //     }
+    // }
+    //
+    // public static class PBKDF2
+    //     extends JDKAlgorithmParameters
+    // {
+    //     PBKDF2Params params;
+    //
+    //     protected byte[] engineGetEncoded()
+    //     {
+    //         try
+    //         {
+    //             return params.getEncoded(ASN1Encodable.DER);
+    //         }
+    //         catch (IOException e)
+    //         {
+    //             throw new RuntimeException("Oooops! " + e.toString());
+    //         }
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //         String format)
+    //     {
+    //         if (isASN1FormatString(format))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //         Class paramSpec)
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == PBEParameterSpec.class)
+    //         {
+    //             return new PBEParameterSpec(params.getSalt(),
+    //                             params.getIterationCount().intValue());
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec paramSpec)
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (!(paramSpec instanceof PBEParameterSpec))
+    //         {
+    //             throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
+    //         }
+    //
+    //         PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
+    //
+    //         this.params = new PBKDF2Params(pbeSpec.getSalt(),
+    //                             pbeSpec.getIterationCount());
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params)
+    //         throws IOException
+    //     {
+    //         this.params = PBKDF2Params.getInstance(ASN1Object.fromByteArray(params));
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params,
+    //         String format)
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format))
+    //         {
+    //             engineInit(params);
+    //             return;
+    //         }
+    //
+    //         throw new IOException("Unknown parameters format in PWRIKEK parameters object");
+    //     }
+    //
+    //     protected String engineToString()
+    //     {
+    //         return "PBKDF2 Parameters";
+    //     }
+    // }
+    // END android-removed
+    
     public static class PKCS12PBE
         extends JDKAlgorithmParameters
     {
         PKCS12PBEParams params;
-
+    
         protected byte[] engineGetEncoded() 
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
             try
             {
-                dOut.writeObject(params);
+                return params.getEncoded(ASN1Encodable.DER);
             }
             catch (IOException e)
             {
                 throw new RuntimeException("Oooops! " + e.toString());
             }
-
-            return bOut.toByteArray();
         }
-
+    
         protected byte[] engineGetEncoded(
             String format) 
         {
-            if (format.equals("ASN.1"))
+            if (isASN1FormatString(format))
             {
                 return engineGetEncoded();
             }
-
+    
             return null;
         }
-
-        protected AlgorithmParameterSpec engineGetParameterSpec(
+    
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
             Class paramSpec) 
             throws InvalidParameterSpecException
         {
@@ -585,10 +474,10 @@
                 return new PBEParameterSpec(params.getIV(),
                                 params.getIterations().intValue());
             }
-
+    
             throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
         }
-
+    
         protected void engineInit(
             AlgorithmParameterSpec paramSpec) 
             throws InvalidParameterSpecException
@@ -597,36 +486,34 @@
             {
                 throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
             }
-
+    
             PBEParameterSpec    pbeSpec = (PBEParameterSpec)paramSpec;
-
+    
             this.params = new PKCS12PBEParams(pbeSpec.getSalt(),
                                 pbeSpec.getIterationCount());
         }
-
+    
         protected void engineInit(
             byte[] params) 
             throws IOException
         {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-
-            this.params = PKCS12PBEParams.getInstance(aIn.readObject());
+            this.params = PKCS12PBEParams.getInstance(ASN1Object.fromByteArray(params));
         }
-
+    
         protected void engineInit(
             byte[] params,
             String format) 
             throws IOException
         {
-            if (format.equals("ASN.1"))
+            if (isASN1FormatString(format))
             {
                 engineInit(params);
                 return;
             }
-
+    
             throw new IOException("Unknown parameters format in PKCS12 PBE parameters object");
         }
-
+    
         protected String engineToString() 
         {
             return "PKCS12 PBE Parameters";
@@ -650,28 +537,22 @@
          */
         protected byte[] engineGetEncoded() 
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-            DHParameter             dhP = new DHParameter(currentSpec.getP(), currentSpec.getG(), currentSpec.getL());
+            DHParameter dhP = new DHParameter(currentSpec.getP(), currentSpec.getG(), currentSpec.getL());
 
             try
             {
-                dOut.writeObject(dhP);
-                dOut.close();
+                return dhP.getEncoded(ASN1Encodable.DER);                
             }
             catch (IOException e)
             {
                 throw new RuntimeException("Error encoding DHParameters");
             }
-
-            return bOut.toByteArray();
         }
 
         protected byte[] engineGetEncoded(
             String format) 
         {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
+            if (isASN1FormatString(format))
             {
                 return engineGetEncoded();
             }
@@ -679,7 +560,7 @@
             return null;
         }
 
-        protected AlgorithmParameterSpec engineGetParameterSpec(
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
             Class paramSpec) 
             throws InvalidParameterSpecException
         {
@@ -707,11 +588,9 @@
             byte[] params) 
             throws IOException
         {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-
             try
             {
-                DHParameter dhP = new DHParameter((ASN1Sequence)aIn.readObject());
+                DHParameter dhP = new DHParameter((ASN1Sequence)ASN1Object.fromByteArray(params));
 
                 if (dhP.getL() != null)
                 {
@@ -737,8 +616,7 @@
             String format) 
             throws IOException
         {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
+            if (isASN1FormatString(format))
             {
                 engineInit(params);
             }
@@ -771,28 +649,22 @@
          */
         protected byte[] engineGetEncoded() 
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-            DSAParameter            dsaP = new DSAParameter(currentSpec.getP(), currentSpec.getQ(), currentSpec.getG());
+            DSAParameter dsaP = new DSAParameter(currentSpec.getP(), currentSpec.getQ(), currentSpec.getG());
 
             try
             {
-                dOut.writeObject(dsaP);
-                dOut.close();
+                return dsaP.getEncoded(ASN1Encodable.DER);
             }
             catch (IOException e)
             {
                 throw new RuntimeException("Error encoding DSAParameters");
             }
-
-            return bOut.toByteArray();
         }
 
         protected byte[] engineGetEncoded(
             String format) 
         {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
+            if (isASN1FormatString(format))
             {
                 return engineGetEncoded();
             }
@@ -800,7 +672,7 @@
             return null;
         }
 
-        protected AlgorithmParameterSpec engineGetParameterSpec(
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
             Class paramSpec) 
             throws InvalidParameterSpecException
         {
@@ -828,11 +700,9 @@
             byte[] params) 
             throws IOException
         {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-
             try
             {
-                DSAParameter dsaP = new DSAParameter((ASN1Sequence)aIn.readObject());
+                DSAParameter dsaP = new DSAParameter((ASN1Sequence)ASN1Object.fromByteArray(params));
 
                 currentSpec = new DSAParameterSpec(dsaP.getP(), dsaP.getQ(), dsaP.getG());
             }
@@ -851,8 +721,7 @@
             String format) 
             throws IOException
         {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
+            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
             {
                 engineInit(params);
             }
@@ -868,363 +737,336 @@
         }
     }
     
-// BEGIN android-removed
-//    public static class GOST3410
-//        extends JDKAlgorithmParameters
-//    {
-//        GOST3410ParameterSpec     currentSpec;
-//        
-//        /**
-//         * Return the X.509 ASN.1 structure GOST3410Parameter.
-//         * <p>
-//         * <pre>
-//         *  GOST3410Parameter ::= SEQUENCE {
-//         *                   prime INTEGER, -- p
-//         *                   subprime INTEGER, -- q
-//         *                   base INTEGER, -- a}
-//         * </pre>
-//         */
-//        protected byte[] engineGetEncoded()
-//        {
-//            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-//            DEROutputStream         dOut = new DEROutputStream(bOut);
-//            GOST3410PublicKeyAlgParameters       gost3410P = new GOST3410PublicKeyAlgParameters(new DERObjectIdentifier(currentSpec.getPublicKeyParamSetOID()), new DERObjectIdentifier(currentSpec.getDigestParamSetOID()), new DERObjectIdentifier(currentSpec.getEncryptionParamSetOID()));
-//            
-//            try
-//            {
-//                dOut.writeObject(gost3410P);
-//                dOut.close();
-//            }
-//            catch (IOException e)
-//            {
-//                throw new RuntimeException("Error encoding GOST3410Parameters");
-//            }
-//            
-//            return bOut.toByteArray();
-//        }
-//        
-//        protected byte[] engineGetEncoded(
-//                String format)
-//        {
-//            if (format.equalsIgnoreCase("X.509")
-//                    || format.equalsIgnoreCase("ASN.1"))
-//            {
-//                return engineGetEncoded();
-//            }
-//            
-//            return null;
-//        }
-//        
-//        protected AlgorithmParameterSpec engineGetParameterSpec(
-//                Class paramSpec)
-//        throws InvalidParameterSpecException
-//        {
-//            if (paramSpec == GOST3410PublicKeyParameterSetSpec.class)
-//            {
-//                return currentSpec;
-//            }
-//            
-//            throw new InvalidParameterSpecException("unknown parameter spec passed to GOST3410 parameters object.");
-//        }
-//        
-//        protected void engineInit(
-//                AlgorithmParameterSpec paramSpec)
-//        throws InvalidParameterSpecException
-//        {
-//            if (!(paramSpec instanceof GOST3410ParameterSpec))
-//            {
-//                throw new InvalidParameterSpecException("GOST3410ParameterSpec required to initialise a GOST3410 algorithm parameters object");
-//            }
-//            
-//            this.currentSpec = (GOST3410ParameterSpec)paramSpec;
-//        }
-//        
-//        protected void engineInit(
-//                byte[] params)
-//        throws IOException
-//        {
-//            ASN1InputStream        dIn = new ASN1InputStream(params);
-//            
-//            try
-//            {
-//                GOST3410PublicKeyAlgParameters gost3410P = new GOST3410PublicKeyAlgParameters((ASN1Sequence)dIn.readObject());
-//                
-//                currentSpec = new GOST3410ParameterSpec(gost3410P.getPublicKeyParamSet().getId(), gost3410P.getDigestParamSet().getId(), gost3410P.getEncryptionParamSet().getId());
-//            }
-//            catch (ClassCastException e)
-//            {
-//                throw new IOException("Not a valid GOST3410 Parameter encoding.");
-//            }
-//            catch (ArrayIndexOutOfBoundsException e)
-//            {
-//                throw new IOException("Not a valid GOST3410 Parameter encoding.");
-//            }
-//        }
-//        
-//        protected void engineInit(
-//                byte[] params,
-//                String format)
-//        throws IOException
-//        {
-//            if (format.equalsIgnoreCase("X.509")
-//                    || format.equalsIgnoreCase("ASN.1"))
-//            {
-//                engineInit(params);
-//            }
-//            else
-//            {
-//                throw new IOException("Unknown parameter format " + format);
-//            }
-//        }
-//        
-//        protected String engineToString()
-//        {
-//            return "GOST3410 Parameters";
-//        }
-//    }
-//
-//    public static class ElGamal
-//        extends JDKAlgorithmParameters
-//    {
-//        ElGamalParameterSpec     currentSpec;
-//
-//        /**
-//         * Return the X.509 ASN.1 structure ElGamalParameter.
-//         * <p>
-//         * <pre>
-//         *  ElGamalParameter ::= SEQUENCE {
-//         *                   prime INTEGER, -- p
-//         *                   base INTEGER, -- g}
-//         * </pre>
-//         */
-//        protected byte[] engineGetEncoded() 
-//        {
-//            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-//            DEROutputStream         dOut = new DEROutputStream(bOut);
-//            ElGamalParameter        elP = new ElGamalParameter(currentSpec.getP(), currentSpec.getG());
-//
-//            try
-//            {
-//                dOut.writeObject(elP);
-//                dOut.close();
-//            }
-//            catch (IOException e)
-//            {
-//                throw new RuntimeException("Error encoding ElGamalParameters");
-//            }
-//
-//            return bOut.toByteArray();
-//        }
-//
-//        protected byte[] engineGetEncoded(
-//            String format) 
-//        {
-//            if (format.equalsIgnoreCase("X.509")
-//                    || format.equalsIgnoreCase("ASN.1"))
-//            {
-//                return engineGetEncoded();
-//            }
-//
-//            return null;
-//        }
-//
-//        protected AlgorithmParameterSpec engineGetParameterSpec(
-//            Class paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (paramSpec == ElGamalParameterSpec.class)
-//            {
-//                return currentSpec;
-//            }
-//            else if (paramSpec == DHParameterSpec.class)
-//            {
-//                return new DHParameterSpec(currentSpec.getP(), currentSpec.getG());
-//            }
-//
-//            throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
-//        }
-//
-//        protected void engineInit(
-//            AlgorithmParameterSpec paramSpec) 
-//            throws InvalidParameterSpecException
-//        {
-//            if (!(paramSpec instanceof ElGamalParameterSpec) && !(paramSpec instanceof DHParameterSpec))
-//            {
-//                throw new InvalidParameterSpecException("DHParameterSpec required to initialise a ElGamal algorithm parameters object");
-//            }
-//
-//            if (paramSpec instanceof ElGamalParameterSpec)
-//            {
-//                this.currentSpec = (ElGamalParameterSpec)paramSpec;
-//            }
-//            else
-//            {
-//                DHParameterSpec s = (DHParameterSpec)paramSpec;
-//                
-//                this.currentSpec = new ElGamalParameterSpec(s.getP(), s.getG());
-//            }
-//        }
-//
-//        protected void engineInit(
-//            byte[] params) 
-//            throws IOException
-//        {
-//            ASN1InputStream        aIn = new ASN1InputStream(params);
-//
-//            try
-//            {
-//                ElGamalParameter elP = new ElGamalParameter((ASN1Sequence)aIn.readObject());
-//
-//                currentSpec = new ElGamalParameterSpec(elP.getP(), elP.getG());
-//            }
-//            catch (ClassCastException e)
-//            {
-//                throw new IOException("Not a valid ElGamal Parameter encoding.");
-//            }
-//            catch (ArrayIndexOutOfBoundsException e)
-//            {
-//                throw new IOException("Not a valid ElGamal Parameter encoding.");
-//            }
-//        }
-//
-//        protected void engineInit(
-//            byte[] params,
-//            String format) 
-//            throws IOException
-//        {
-//            if (format.equalsIgnoreCase("X.509")
-//                    || format.equalsIgnoreCase("ASN.1"))
-//            {
-//                engineInit(params);
-//            }
-//            else
-//            {
-//                throw new IOException("Unknown parameter format " + format);
-//            }
-//        }
-//
-//        protected String engineToString() 
-//        {
-//            return "ElGamal Parameters";
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // public static class GOST3410
+    //     extends JDKAlgorithmParameters
+    // {
+    //     GOST3410ParameterSpec     currentSpec;
+    //
+    //     /**
+    //      * Return the X.509 ASN.1 structure GOST3410Parameter.
+    //      * <p>
+    //      * <pre>
+    //      *  GOST3410Parameter ::= SEQUENCE {
+    //      *                   prime INTEGER, -- p
+    //      *                   subprime INTEGER, -- q
+    //      *                   base INTEGER, -- a}
+    //      * </pre>
+    //      */
+    //     protected byte[] engineGetEncoded()
+    //     {
+    //         GOST3410PublicKeyAlgParameters gost3410P = new GOST3410PublicKeyAlgParameters(new DERObjectIdentifier(currentSpec.getPublicKeyParamSetOID()), new DERObjectIdentifier(currentSpec.getDigestParamSetOID()), new DERObjectIdentifier(currentSpec.getEncryptionParamSetOID()));
+    //
+    //         try
+    //         {
+    //             return gost3410P.getEncoded(ASN1Encodable.DER);
+    //         }
+    //         catch (IOException e)
+    //         {
+    //             throw new RuntimeException("Error encoding GOST3410Parameters");
+    //         }
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //             String format)
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //             Class paramSpec)
+    //     throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == GOST3410PublicKeyParameterSetSpec.class)
+    //         {
+    //             return currentSpec;
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to GOST3410 parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //             AlgorithmParameterSpec paramSpec)
+    //     throws InvalidParameterSpecException
+    //     {
+    //         if (!(paramSpec instanceof GOST3410ParameterSpec))
+    //         {
+    //             throw new InvalidParameterSpecException("GOST3410ParameterSpec required to initialise a GOST3410 algorithm parameters object");
+    //         }
+    //
+    //         this.currentSpec = (GOST3410ParameterSpec)paramSpec;
+    //     }
+    //
+    //     protected void engineInit(
+    //             byte[] params)
+    //     throws IOException
+    //     {
+    //         try
+    //         {
+    //             ASN1Sequence seq = (ASN1Sequence) ASN1Object.fromByteArray(params);
+    //
+    //             this.currentSpec = GOST3410ParameterSpec.fromPublicKeyAlg(
+    //                 new GOST3410PublicKeyAlgParameters(seq));
+    //         }
+    //         catch (ClassCastException e)
+    //         {
+    //             throw new IOException("Not a valid GOST3410 Parameter encoding.");
+    //         }
+    //         catch (ArrayIndexOutOfBoundsException e)
+    //         {
+    //             throw new IOException("Not a valid GOST3410 Parameter encoding.");
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //             byte[] params,
+    //             String format)
+    //     throws IOException
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             engineInit(params);
+    //         }
+    //         else
+    //         {
+    //             throw new IOException("Unknown parameter format " + format);
+    //         }
+    //     }
+    //
+    //     protected String engineToString()
+    //     {
+    //         return "GOST3410 Parameters";
+    //     }
+    // }
 
-    public static class IES
-        extends JDKAlgorithmParameters
-    {
-        IESParameterSpec     currentSpec;
-
-        /**
-         * in the abscence of a standard way of doing it this will do for
-         * now...
-         */
-        protected byte[] engineGetEncoded() 
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            try
-            {
-                ASN1EncodableVector    v = new ASN1EncodableVector();
-
-                v.add(new DEROctetString(currentSpec.getDerivationV()));
-                v.add(new DEROctetString(currentSpec.getEncodingV()));
-                v.add(new DERInteger(currentSpec.getMacKeySize()));
-
-                dOut.writeObject(new DERSequence(v));
-                dOut.close();
-            }
-            catch (IOException e)
-            {
-                throw new RuntimeException("Error encoding IESParameters");
-            }
-
-            return bOut.toByteArray();
-        }
-
-        protected byte[] engineGetEncoded(
-            String format) 
-        {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
-            {
-                return engineGetEncoded();
-            }
-
-            return null;
-        }
-
-        protected AlgorithmParameterSpec engineGetParameterSpec(
-            Class paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            if (paramSpec == IESParameterSpec.class)
-            {
-                return currentSpec;
-            }
-
-            throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
-        }
-
-        protected void engineInit(
-            AlgorithmParameterSpec paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            if (!(paramSpec instanceof IESParameterSpec))
-            {
-                throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object");
-            }
-
-            this.currentSpec = (IESParameterSpec)paramSpec;
-        }
-
-        protected void engineInit(
-            byte[] params) 
-            throws IOException
-        {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-
-            try
-            {
-                ASN1Sequence    s = (ASN1Sequence)aIn.readObject();
-
-                this.currentSpec = new IESParameterSpec(
-                                        ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
-                                        ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
-                                        ((DERInteger)s.getObjectAt(0)).getValue().intValue());
-            }
-            catch (ClassCastException e)
-            {
-                throw new IOException("Not a valid IES Parameter encoding.");
-            }
-            catch (ArrayIndexOutOfBoundsException e)
-            {
-                throw new IOException("Not a valid IES Parameter encoding.");
-            }
-        }
-
-        protected void engineInit(
-            byte[] params,
-            String format) 
-            throws IOException
-        {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
-            {
-                engineInit(params);
-            }
-            else
-            {
-                throw new IOException("Unknown parameter format " + format);
-            }
-        }
-
-        protected String engineToString() 
-        {
-            return "IES Parameters";
-        }
-    }
+    // public static class ElGamal
+    //     extends JDKAlgorithmParameters
+    // {
+    //     ElGamalParameterSpec     currentSpec;
+    //
+    //     /**
+    //      * Return the X.509 ASN.1 structure ElGamalParameter.
+    //      * <p>
+    //      * <pre>
+    //      *  ElGamalParameter ::= SEQUENCE {
+    //      *                   prime INTEGER, -- p
+    //      *                   base INTEGER, -- g}
+    //      * </pre>
+    //      */
+    //     protected byte[] engineGetEncoded() 
+    //     {
+    //         ElGamalParameter elP = new ElGamalParameter(currentSpec.getP(), currentSpec.getG());
+    //
+    //         try
+    //         {
+    //             return elP.getEncoded(ASN1Encodable.DER);
+    //         }
+    //         catch (IOException e)
+    //         {
+    //             throw new RuntimeException("Error encoding ElGamalParameters");
+    //         }
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //         String format) 
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //         Class paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == ElGamalParameterSpec.class)
+    //         {
+    //             return currentSpec;
+    //         }
+    //         else if (paramSpec == DHParameterSpec.class)
+    //         {
+    //             return new DHParameterSpec(currentSpec.getP(), currentSpec.getG());
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (!(paramSpec instanceof ElGamalParameterSpec) && !(paramSpec instanceof DHParameterSpec))
+    //         {
+    //             throw new InvalidParameterSpecException("DHParameterSpec required to initialise a ElGamal algorithm parameters object");
+    //         }
+    //
+    //         if (paramSpec instanceof ElGamalParameterSpec)
+    //         {
+    //             this.currentSpec = (ElGamalParameterSpec)paramSpec;
+    //         }
+    //         else
+    //         {
+    //             DHParameterSpec s = (DHParameterSpec)paramSpec;
+    //
+    //             this.currentSpec = new ElGamalParameterSpec(s.getP(), s.getG());
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params) 
+    //         throws IOException
+    //     {
+    //         try
+    //         {
+    //             ElGamalParameter elP = new ElGamalParameter((ASN1Sequence)ASN1Object.fromByteArray(params));
+    //
+    //             currentSpec = new ElGamalParameterSpec(elP.getP(), elP.getG());
+    //         }
+    //         catch (ClassCastException e)
+    //         {
+    //             throw new IOException("Not a valid ElGamal Parameter encoding.");
+    //         }
+    //         catch (ArrayIndexOutOfBoundsException e)
+    //         {
+    //             throw new IOException("Not a valid ElGamal Parameter encoding.");
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params,
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             engineInit(params);
+    //         }
+    //         else
+    //         {
+    //             throw new IOException("Unknown parameter format " + format);
+    //         }
+    //     }
+    //
+    //     protected String engineToString() 
+    //     {
+    //         return "ElGamal Parameters";
+    //     }
+    // }
+    //
+    // public static class IES
+    //     extends JDKAlgorithmParameters
+    // {
+    //     IESParameterSpec     currentSpec;
+    //
+    //     /**
+    //      * in the absence of a standard way of doing it this will do for
+    //      * now...
+    //      */
+    //     protected byte[] engineGetEncoded() 
+    //     {
+    //         try
+    //         {
+    //             ASN1EncodableVector v = new ASN1EncodableVector();
+    //
+    //             v.add(new DEROctetString(currentSpec.getDerivationV()));
+    //             v.add(new DEROctetString(currentSpec.getEncodingV()));
+    //             v.add(new DERInteger(currentSpec.getMacKeySize()));
+    //
+    //             return new DERSequence(v).getEncoded(ASN1Encodable.DER);
+    //         }
+    //         catch (IOException e)
+    //         {
+    //             throw new RuntimeException("Error encoding IESParameters");
+    //         }
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //         String format) 
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //         Class paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == IESParameterSpec.class)
+    //         {
+    //             return currentSpec;
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (!(paramSpec instanceof IESParameterSpec))
+    //         {
+    //             throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object");
+    //         }
+    //
+    //         this.currentSpec = (IESParameterSpec)paramSpec;
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params) 
+    //         throws IOException
+    //     {
+    //         try
+    //         {
+    //             ASN1Sequence s = (ASN1Sequence)ASN1Object.fromByteArray(params);
+    //
+    //             this.currentSpec = new IESParameterSpec(
+    //                                     ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+    //                                     ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+    //                                     ((DERInteger)s.getObjectAt(0)).getValue().intValue());
+    //         }
+    //         catch (ClassCastException e)
+    //         {
+    //             throw new IOException("Not a valid IES Parameter encoding.");
+    //         }
+    //         catch (ArrayIndexOutOfBoundsException e)
+    //         {
+    //             throw new IOException("Not a valid IES Parameter encoding.");
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params,
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             engineInit(params);
+    //         }
+    //         else
+    //         {
+    //             throw new IOException("Unknown parameter format " + format);
+    //         }
+    //     }
+    //
+    //     protected String engineToString() 
+    //     {
+    //         return "IES Parameters";
+    //     }
+    // }
+    // END android-removed
     
     public static class OAEP
         extends JDKAlgorithmParameters
@@ -1232,21 +1074,21 @@
         OAEPParameterSpec     currentSpec;
     
         /**
-         * Return the PKCS#1 ASN.1 structure RSA-ES-OAEP-params.
+         * Return the PKCS#1 ASN.1 structure RSAES-OAEP-params.
          */
         protected byte[] engineGetEncoded() 
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-            // BEGIN android-changed
             AlgorithmIdentifier     hashAlgorithm = new AlgorithmIdentifier(
                                                             JCEDigestUtil.getOID(currentSpec.getDigestAlgorithm()),
-                                                            DERNull.THE_ONE);
+                                                            // BEGIN android-changed
+                                                            DERNull.INSTANCE);
+                                                            // END android-changed
             MGF1ParameterSpec       mgfSpec = (MGF1ParameterSpec)currentSpec.getMGFParameters();
             AlgorithmIdentifier     maskGenAlgorithm = new AlgorithmIdentifier(
                                                             PKCSObjectIdentifiers.id_mgf1, 
-                                                            new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.THE_ONE));
-            // END android-changed
+                                                            // BEGIN android-changed
+                                                            new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
+                                                            // END android-changed
             PSource.PSpecified      pSource = (PSource.PSpecified)currentSpec.getPSource();
             AlgorithmIdentifier     pSourceAlgorithm = new AlgorithmIdentifier(
                                                             PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(pSource.getValue()));
@@ -1254,22 +1096,18 @@
     
             try
             {
-                dOut.writeObject(oaepP);
-                dOut.close();
+                return oaepP.getEncoded(ASN1Encodable.DER);
             }
             catch (IOException e)
             {
                 throw new RuntimeException("Error encoding OAEPParameters");
             }
-    
-            return bOut.toByteArray();
         }
     
         protected byte[] engineGetEncoded(
             String format) 
         {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
+            if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
             {
                 return engineGetEncoded();
             }
@@ -1277,11 +1115,11 @@
             return null;
         }
     
-        protected AlgorithmParameterSpec engineGetParameterSpec(
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(
             Class paramSpec) 
             throws InvalidParameterSpecException
         {
-            if (paramSpec == OAEPParameterSpec.class && currentSpec instanceof OAEPParameterSpec)
+            if (paramSpec == OAEPParameterSpec.class && currentSpec != null)
             {
                 return currentSpec;
             }
@@ -1305,12 +1143,10 @@
             byte[] params) 
             throws IOException
         {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-    
             try
             {
-                RSAESOAEPparams oaepP = new RSAESOAEPparams((ASN1Sequence)aIn.readObject());
-    
+                RSAESOAEPparams oaepP = new RSAESOAEPparams((ASN1Sequence)ASN1Object.fromByteArray(params));
+
                 currentSpec = new OAEPParameterSpec(
                                        oaepP.getHashAlgorithm().getObjectId().getId(), 
                                        oaepP.getMaskGenAlgorithm().getObjectId().getId(), 
@@ -1349,115 +1185,116 @@
         }
     }
     
-    public static class PSS
-        extends JDKAlgorithmParameters
-    {  
-        PSSParameterSpec     currentSpec;
-    
-        /**
-         * Return the PKCS#1 ASN.1 structure RSA-ES-OAEP-params.
-         */
-        protected byte[] engineGetEncoded() 
-            throws IOException
-        {
-            PSSParameterSpec    pssSpec = (PSSParameterSpec)currentSpec;
-            // BEGIN android-changed
-            AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
-                                                JCEDigestUtil.getOID(pssSpec.getDigestAlgorithm()),
-                                                DERNull.THE_ONE);
-            MGF1ParameterSpec   mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
-            AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
-                                                PKCSObjectIdentifiers.id_mgf1, 
-                                                new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.THE_ONE));
-            // END android-changed
-            RSASSAPSSparams     pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new DERInteger(pssSpec.getSaltLength()), new DERInteger(pssSpec.getTrailerField()));
-            
-            return pssP.getEncoded("DER");
-        }
-    
-        protected byte[] engineGetEncoded(
-            String format) 
-            throws IOException
-        {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
-            {
-                return engineGetEncoded();
-            }
-    
-            return null;
-        }
-    
-        protected AlgorithmParameterSpec engineGetParameterSpec(
-            Class paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            if (paramSpec == PSSParameterSpec.class && currentSpec instanceof PSSParameterSpec)
-            {
-                return currentSpec;
-            }
-    
-            throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
-        }
-    
-        protected void engineInit(
-            AlgorithmParameterSpec paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            if (!(paramSpec instanceof PSSParameterSpec))
-            {
-                throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object");
-            }
-    
-            this.currentSpec = (PSSParameterSpec)paramSpec;
-        }
-    
-        protected void engineInit(
-            byte[] params) 
-            throws IOException
-        {
-            ASN1InputStream        aIn = new ASN1InputStream(params);
-    
-            try
-            {
-                RSASSAPSSparams pssP = new RSASSAPSSparams((ASN1Sequence)aIn.readObject());
-    
-                currentSpec = new PSSParameterSpec(
-                                       pssP.getHashAlgorithm().getObjectId().getId(), 
-                                       pssP.getMaskGenAlgorithm().getObjectId().getId(), 
-                                       new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getObjectId().getId()),
-                                       pssP.getSaltLength().getValue().intValue(),
-                                       pssP.getTrailerField().getValue().intValue());
-            }
-            catch (ClassCastException e)
-            {
-                throw new IOException("Not a valid PSS Parameter encoding.");
-            }
-            catch (ArrayIndexOutOfBoundsException e)
-            {
-                throw new IOException("Not a valid PSS Parameter encoding.");
-            }
-        }
-    
-        protected void engineInit(
-            byte[] params,
-            String format) 
-            throws IOException
-        {
-            if (format.equalsIgnoreCase("X.509")
-                    || format.equalsIgnoreCase("ASN.1"))
-            {
-                engineInit(params);
-            }
-            else
-            {
-                throw new IOException("Unknown parameter format " + format);
-            }
-        }
-    
-        protected String engineToString() 
-        {
-            return "PSS Parameters";
-        }
-    }
+    // BEGIN android-removed
+    // public static class PSS
+    //     extends JDKAlgorithmParameters
+    // {  
+    //     PSSParameterSpec     currentSpec;
+    //
+    //     /**
+    //      * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params.
+    //      */
+    //     protected byte[] engineGetEncoded() 
+    //         throws IOException
+    //     {
+    //         PSSParameterSpec    pssSpec = currentSpec;
+    //         AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
+    //                                             JCEDigestUtil.getOID(pssSpec.getDigestAlgorithm()),
+    //                                             // BEGIN android-changed
+    //                                             DERNull.INSTANCE);
+    //                                             // END android-changed
+    //         MGF1ParameterSpec   mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
+    //         AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+    //                                             PKCSObjectIdentifiers.id_mgf1, 
+    //                                             // BEGIN android-changed
+    //                                             new AlgorithmIdentifier(JCEDigestUtil.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
+    //                                             // END android-changed
+    //         RSASSAPSSparams     pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new DERInteger(pssSpec.getSaltLength()), new DERInteger(pssSpec.getTrailerField()));
+    //
+    //         return pssP.getEncoded("DER");
+    //     }
+    //
+    //     protected byte[] engineGetEncoded(
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (format.equalsIgnoreCase("X.509")
+    //                 || format.equalsIgnoreCase("ASN.1"))
+    //         {
+    //             return engineGetEncoded();
+    //         }
+    //
+    //         return null;
+    //     }
+    //
+    //     protected AlgorithmParameterSpec localEngineGetParameterSpec(
+    //         Class paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (paramSpec == PSSParameterSpec.class && currentSpec != null)
+    //         {
+    //             return currentSpec;
+    //         }
+    //
+    //         throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
+    //     }
+    //
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec paramSpec) 
+    //         throws InvalidParameterSpecException
+    //     {
+    //         if (!(paramSpec instanceof PSSParameterSpec))
+    //         {
+    //             throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object");
+    //         }
+    //
+    //         this.currentSpec = (PSSParameterSpec)paramSpec;
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params) 
+    //         throws IOException
+    //     {
+    //         try
+    //         {
+    //             RSASSAPSSparams pssP = new RSASSAPSSparams((ASN1Sequence)ASN1Object.fromByteArray(params));
+    //
+    //             currentSpec = new PSSParameterSpec(
+    //                                    pssP.getHashAlgorithm().getObjectId().getId(), 
+    //                                    pssP.getMaskGenAlgorithm().getObjectId().getId(), 
+    //                                    new MGF1ParameterSpec(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getObjectId().getId()),
+    //                                    pssP.getSaltLength().getValue().intValue(),
+    //                                    pssP.getTrailerField().getValue().intValue());
+    //         }
+    //         catch (ClassCastException e)
+    //         {
+    //             throw new IOException("Not a valid PSS Parameter encoding.");
+    //         }
+    //         catch (ArrayIndexOutOfBoundsException e)
+    //         {
+    //             throw new IOException("Not a valid PSS Parameter encoding.");
+    //         }
+    //     }
+    //
+    //     protected void engineInit(
+    //         byte[] params,
+    //         String format) 
+    //         throws IOException
+    //     {
+    //         if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+    //         {
+    //             engineInit(params);
+    //         }
+    //         else
+    //         {
+    //             throw new IOException("Unknown parameter format " + format);
+    //         }
+    //     }
+    //
+    //     protected String engineToString() 
+    //     {
+    //         return "PSS Parameters";
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java b/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java
index 111ab5b..5d016d0 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java
@@ -1,14 +1,5 @@
 package org.bouncycastle.jce.provider;
 
-import java.math.BigInteger;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.spec.DSAParameterSpec;
-import java.security.spec.DSAPrivateKeySpec;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERInteger;
@@ -20,14 +11,25 @@
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.util.Enumeration;
+
 public class JDKDSAPrivateKey
     implements DSAPrivateKey, PKCS12BagAttributeCarrier
 {
+    private static final long serialVersionUID = -4677259546958385734L;
+
     BigInteger          x;
     DSAParams           dsaSpec;
 
-    private Hashtable   pkcs12Attributes = new Hashtable();
-    private Vector      pkcs12Ordering = new Vector();
+    private PKCS12BagAttributeCarrierImpl   attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
     protected JDKDSAPrivateKey()
     {
@@ -117,23 +119,51 @@
             && this.getParams().getP().equals(other.getParams().getP()) 
             && this.getParams().getQ().equals(other.getParams().getQ());
     }
+
+    public int hashCode()
+    {
+        return this.getX().hashCode() ^ this.getParams().getG().hashCode()
+                ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode();
+    }
     
     public void setBagAttribute(
         DERObjectIdentifier oid,
         DEREncodable        attribute)
     {
-        pkcs12Attributes.put(oid, attribute);
-        pkcs12Ordering.addElement(oid);
+        attrCarrier.setBagAttribute(oid, attribute);
     }
 
     public DEREncodable getBagAttribute(
         DERObjectIdentifier oid)
     {
-        return (DEREncodable)pkcs12Attributes.get(oid);
+        return attrCarrier.getBagAttribute(oid);
     }
 
     public Enumeration getBagAttributeKeys()
     {
-        return pkcs12Ordering.elements();
+        return attrCarrier.getBagAttributeKeys();
+    }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        this.x = (BigInteger)in.readObject();
+        this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject());
+        this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+        
+        attrCarrier.readObject(in);
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.writeObject(x);
+        out.writeObject(dsaSpec.getP());
+        out.writeObject(dsaSpec.getQ());
+        out.writeObject(dsaSpec.getG());
+
+        attrCarrier.writeObject(out);
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java b/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java
index 56e8470..392f532 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java
@@ -1,23 +1,29 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAParameterSpec;
-import java.security.spec.DSAPublicKeySpec;
-
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPublicKeySpec;
+
 public class JDKDSAPublicKey
     implements DSAPublicKey
 {
+    private static final long serialVersionUID = 1752452449903495175L;
+
     private BigInteger      y;
     private DSAParams       dsaSpec;
 
@@ -53,8 +59,8 @@
     JDKDSAPublicKey(
         SubjectPublicKeyInfo    info)
     {
-        DSAParameter            params = new DSAParameter((ASN1Sequence)info.getAlgorithmId().getParameters());
-        DERInteger              derY = null;
+
+        DERInteger              derY;
 
         try
         {
@@ -66,7 +72,18 @@
         }
 
         this.y = derY.getValue();
-        this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG());
+
+        if (isNotNull(info.getAlgorithmId().getParameters()))
+        {
+            DSAParameter params = new DSAParameter((ASN1Sequence)info.getAlgorithmId().getParameters());
+            
+            this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG());
+        }
+    }
+
+    private boolean isNotNull(DEREncodable parameters)
+    {
+        return parameters != null && !DERNull.INSTANCE.equals(parameters);
     }
 
     public String getAlgorithm()
@@ -81,9 +98,12 @@
 
     public byte[] getEncoded()
     {
-        SubjectPublicKeyInfo    info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG()).getDERObject()), new DERInteger(y));
+        if (dsaSpec == null)
+        {
+            return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa), new DERInteger(y)).getDEREncoded();
+        }
 
-        return info.getDEREncoded();
+        return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG()).getDERObject()), new DERInteger(y)).getDEREncoded();
     }
 
     public DSAParams getParams()
@@ -106,7 +126,13 @@
 
         return buf.toString();
     }
-    
+
+    public int hashCode()
+    {
+        return this.getY().hashCode() ^ this.getParams().getG().hashCode() 
+                ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode();
+    }
+
     public boolean equals(
         Object o)
     {
@@ -122,4 +148,22 @@
             && this.getParams().getP().equals(other.getParams().getP()) 
             && this.getParams().getQ().equals(other.getParams().getQ());
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        this.y = (BigInteger)in.readObject();
+        this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject());
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.writeObject(y);
+        out.writeObject(dsaSpec.getP());
+        out.writeObject(dsaSpec.getQ());
+        out.writeObject(dsaSpec.getG());
+    }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKDSASigner.java b/src/main/java/org/bouncycastle/jce/provider/JDKDSASigner.java
index 9a5eb66..9402743 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKDSASigner.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKDSASigner.java
@@ -1,22 +1,20 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.Signature;
 import java.security.SignatureException;
+import java.security.SignatureSpi;
 import java.security.interfaces.DSAKey;
 import java.security.spec.AlgorithmParameterSpec;
 
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
@@ -24,34 +22,31 @@
 import org.bouncycastle.crypto.DSA;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.digests.SHA224Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// END android-removed
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.DSASigner;
 // BEGIN android-removed
-// import org.bouncycastle.crypto.signers.ECDSASigner;
-// import org.bouncycastle.crypto.signers.ECNRSigner;
-// import org.bouncycastle.jce.interfaces.ECKey;
-// import org.bouncycastle.jce.interfaces.ECPublicKey;
 // import org.bouncycastle.jce.interfaces.GOST3410Key;
 // END android-removed
+import org.bouncycastle.jce.provider.util.NullDigest;
 
 public class JDKDSASigner
-    extends Signature implements PKCSObjectIdentifiers, X509ObjectIdentifiers
+    extends SignatureSpi
+    implements PKCSObjectIdentifiers, X509ObjectIdentifiers
 {
     private Digest                  digest;
     private DSA                     signer;
     private SecureRandom            random;
 
     protected JDKDSASigner(
-        String                  name,
         Digest                  digest,
         DSA                     signer)
     {
-        super(name);
-
         this.digest = digest;
         this.signer = signer;
     }
@@ -60,14 +55,10 @@
         PublicKey   publicKey)
         throws InvalidKeyException
     {
-        CipherParameters    param = null;
+        CipherParameters    param;
 
         // BEGIN android-removed
-        // if (publicKey instanceof ECPublicKey)
-        // {
-        //     param = ECUtil.generatePublicKeyParameter(publicKey);
-        // }
-        // else if (publicKey instanceof GOST3410Key)
+        // if (publicKey instanceof GOST3410Key)
         // {
         //     param = GOST3410Util.generatePublicKeyParameter(publicKey);
         // }
@@ -87,16 +78,7 @@
 
                 publicKey = JDKKeyFactory.createPublicKeyFromDERStream(bytes);
 
-                // BEGIN android-removed
-                // if (publicKey instanceof ECPublicKey)
-                // {
-                //     param = ECUtil.generatePublicKeyParameter(publicKey);
-                // }
-                // else if (publicKey instanceof DSAKey)
-                // END android-removed
-                // BEGIN android-added
                 if (publicKey instanceof DSAKey)
-                // END android-added
                 {
                     param = DSAUtil.generatePublicKeyParameter(publicKey);
                 }
@@ -128,14 +110,10 @@
         PrivateKey  privateKey)
         throws InvalidKeyException
     {
-        CipherParameters    param = null;
+        CipherParameters    param;
 
         // BEGIN android-removed
-        // if (privateKey instanceof ECKey)
-        // {
-        //     param = ECUtil.generatePrivateKeyParameter(privateKey);
-        // }
-        // else if (privateKey instanceof GOST3410Key)
+        // if (privateKey instanceof GOST3410Key)
         // {
         //     param = GOST3410Util.generatePrivateKeyParameter(privateKey);
         // }
@@ -147,16 +125,13 @@
         // }
         // END android-removed
 
-        digest.reset();
-
         if (random != null)
         {
-            signer.init(true, new ParametersWithRandom(param, random));
+            param = new ParametersWithRandom(param, random);
         }
-        else
-        {
-            signer.init(true, param);
-        }
+
+        digest.reset();
+        signer.init(true, param);
     }
 
     protected void engineUpdate(
@@ -246,31 +221,19 @@
         BigInteger  s)
         throws IOException
     {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-        ASN1EncodableVector     v = new ASN1EncodableVector();
-
-        v.add(new DERInteger(r));
-        v.add(new DERInteger(s));
-
-        dOut.writeObject(new DERSequence(v));
-
-        return bOut.toByteArray();
+        DERInteger[] rs = new DERInteger[]{ new DERInteger(r), new DERInteger(s) };
+        return new DERSequence(rs).getEncoded(ASN1Encodable.DER);
     }
 
     private BigInteger[] derDecode(
         byte[]  encoding)
         throws IOException
     {
-        ASN1InputStream         aIn = new ASN1InputStream(encoding);
-        ASN1Sequence            s = (ASN1Sequence)aIn.readObject();
-
-        BigInteger[]            sig = new BigInteger[2];
-
-        sig[0] = ((DERInteger)s.getObjectAt(0)).getValue();
-        sig[1] = ((DERInteger)s.getObjectAt(1)).getValue();
-
-        return sig;
+        ASN1Sequence s = (ASN1Sequence)ASN1Object.fromByteArray(encoding);
+        return new BigInteger[]{
+            ((DERInteger)s.getObjectAt(0)).getValue(),
+            ((DERInteger)s.getObjectAt(1)).getValue()
+        };
     }
 
     static public class stdDSA
@@ -278,149 +241,54 @@
     {
         public stdDSA()
         {
-            super("SHA1withDSA", new SHA1Digest(), new DSASigner());
+            super(new SHA1Digest(), new DSASigner());
         }
     }
+    
+    // BEGIN android-removed
+    // static public class dsa224
+    //     extends JDKDSASigner
+    // {
+    //     public dsa224()
+    //     {
+    //         super(new SHA224Digest(), new DSASigner());
+    //     }
+    // }
+    //
+    // static public class dsa256
+    //     extends JDKDSASigner
+    // {
+    //     public dsa256()
+    //     {
+    //         super(new SHA256Digest(), new DSASigner());
+    //     }
+    // }
+    //
+    // static public class dsa384
+    //     extends JDKDSASigner
+    // {
+    //     public dsa384()
+    //     {
+    //         super(new SHA384Digest(), new DSASigner());
+    //     }
+    // }
+    //
+    // static public class dsa512
+    //     extends JDKDSASigner
+    // {
+    //     public dsa512()
+    //     {
+    //         super(new SHA512Digest(), new DSASigner());
+    //     }
+    // }
+    // END android-removed
 
     static public class noneDSA
         extends JDKDSASigner
     {
         public noneDSA()
         {
-            super("NONEwithDSA", new NullDigest(), new DSASigner());
-        }
-    }
-    
-// BEGIN android-removed
-//    static public class ecDSA
-//        extends JDKDSASigner
-//    {
-//        public ecDSA()
-//        {
-//            super("SHA1withECDSA", new SHA1Digest(), new ECDSASigner());
-//        }
-//    }
-//    
-//    static public class ecDSA224
-//        extends JDKDSASigner
-//    {
-//        public ecDSA224()
-//        {
-//            super("SHA224withECDSA", new SHA224Digest(), new ECDSASigner());
-//        }
-//    }
-//    
-//    static public class ecDSA256
-//        extends JDKDSASigner
-//    {
-//        public ecDSA256()
-//        {
-//            super("SHA256withECDSA", new SHA256Digest(), new ECDSASigner());
-//        }
-//    }
-//    
-//    static public class ecDSA384
-//        extends JDKDSASigner
-//    {
-//        public ecDSA384()
-//        {
-//            super("SHA384withECDSA", new SHA384Digest(), new ECDSASigner());
-//        }
-//    }
-//    
-//    static public class ecDSA512
-//        extends JDKDSASigner
-//    {
-//        public ecDSA512()
-//        {
-//            super("SHA512withECDSA", new SHA512Digest(), new ECDSASigner());
-//        }
-//    }
-//    
-//
-//    static public class ecNR 
-//        extends JDKDSASigner
-//    {
-//        public ecNR()
-//        {
-//            super("SHA1withECNR", new SHA1Digest(), new ECNRSigner());
-//        }
-//    }
-//
-//    static public class ecNR224 
-//        extends JDKDSASigner
-//    {
-//        public ecNR224()
-//        {
-//            super("SHA224withECNR", new SHA224Digest(), new ECNRSigner());
-//        }
-//    }
-//
-//    static public class ecNR256 
-//        extends JDKDSASigner
-//    {
-//        public ecNR256()
-//        {
-//            super("SHA256withECNR", new SHA256Digest(), new ECNRSigner());
-//        }
-//    }
-//
-//    static public class ecNR384 
-//        extends JDKDSASigner
-//    {
-//        public ecNR384()
-//        {
-//            super("SHA384withECNR", new SHA384Digest(), new ECNRSigner());
-//        }
-//    }
-//
-//    static public class ecNR512 
-//        extends JDKDSASigner
-//    {
-//        public ecNR512()
-//        {
-//            super("SHA512withECNR", new SHA512Digest(), new ECNRSigner());
-//        }
-//    }
-// END android-removed
-    
-    private static class NullDigest
-        implements Digest
-    {
-        private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        public String getAlgorithmName()
-        {
-            return "NULL";
-        }
-    
-        public int getDigestSize()
-        {
-            return bOut.size();
-        }
-    
-        public void update(byte in)
-        {
-            bOut.write(in);
-        }
-    
-        public void update(byte[] in, int inOff, int len)
-        {
-            bOut.write(in, inOff, len);
-        }
-    
-        public int doFinal(byte[] out, int outOff)
-        {
-            byte[] res = bOut.toByteArray();
-            
-            System.arraycopy(res, 0, out, outOff, res.length);
-            
-            return res.length;
-        }
-    
-        public void reset()
-        {
-            bOut.reset();
+            super(new NullDigest(), new DSASigner());
         }
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKDigestSignature.java b/src/main/java/org/bouncycastle/jce/provider/JDKDigestSignature.java
index 5eea220..380dcd1 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKDigestSignature.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKDigestSignature.java
@@ -1,16 +1,18 @@
 package org.bouncycastle.jce.provider;
 
 import java.io.IOException;
+import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
-import java.security.Signature;
 import java.security.SignatureException;
+import java.security.SignatureSpi;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.AlgorithmParameterSpec;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -23,8 +25,8 @@
 import org.bouncycastle.crypto.Digest;
 // BEGIN android-removed
 // import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
 // END android-removed
-import org.bouncycastle.crypto.digests.MD4Digest;
 import org.bouncycastle.crypto.digests.MD5Digest;
 // BEGIN android-removed
 // import org.bouncycastle.crypto.digests.RIPEMD128Digest;
@@ -32,31 +34,42 @@
 // import org.bouncycastle.crypto.digests.RIPEMD256Digest;
 // END android-removed
 import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.digests.SHA224Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// END android-removed
 import org.bouncycastle.crypto.digests.SHA256Digest;
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.encodings.PKCS1Encoding;
-import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.jce.provider.util.NullDigest;
 
 public class JDKDigestSignature
-    extends Signature implements PKCSObjectIdentifiers, X509ObjectIdentifiers
+    extends SignatureSpi
 {
     private Digest                  digest;
     private AsymmetricBlockCipher   cipher;
     private AlgorithmIdentifier     algId;
-    
+
+    // care - this constructor is actually used by outside organisations
     protected JDKDigestSignature(
-        String                  name,
+        Digest                  digest,
+        AsymmetricBlockCipher   cipher)
+    {
+        this.digest = digest;
+        this.cipher = cipher;
+        this.algId = null;
+    }
+
+    // care - this constructor is actually used by outside organisations
+    protected JDKDigestSignature(
         DERObjectIdentifier     objId,
         Digest                  digest,
         AsymmetricBlockCipher   cipher)
     {
-        super(name);
-
         this.digest = digest;
         this.cipher = cipher;
-        this.algId = new AlgorithmIdentifier(objId);
+        this.algId = new AlgorithmIdentifier(objId, DERNull.INSTANCE);
     }
 
     protected void engineInitVerify(
@@ -226,13 +239,24 @@
     protected Object engineGetParameter(
         String      param)
     {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
+        return null;
+    }
+
+    protected AlgorithmParameters engineGetParameters()
+    {
+        return null;
     }
 
     private byte[] derEncode(
         byte[]  hash)
         throws IOException
     {
+        if (algId == null)
+        {
+            // For raw RSA, the DigestInfo must be prepared externally
+            return hash;
+        }
+
         DigestInfo              dInfo = new DigestInfo(algId, hash);
 
         return dInfo.getEncoded(ASN1Encodable.DER);
@@ -243,43 +267,45 @@
     {
         public SHA1WithRSAEncryption()
         {
-            super("SHA1withRSA", id_SHA1, new SHA1Digest(), new PKCS1Encoding(new RSAEngine()));
+            super(X509ObjectIdentifiers.id_SHA1, new SHA1Digest(), new PKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
-    static public class SHA224WithRSAEncryption
-        extends JDKDigestSignature
-    {
-        public SHA224WithRSAEncryption()
-        {
-            super("SHA224withRSA", NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new RSAEngine()));
-        }
-    }
+    // BEGIN android-removed
+    // static public class SHA224WithRSAEncryption
+    //     extends JDKDigestSignature
+    // {
+    //     public SHA224WithRSAEncryption()
+    //     {
+    //         super(NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    // END android-removed
 
     static public class SHA256WithRSAEncryption
         extends JDKDigestSignature
     {
         public SHA256WithRSAEncryption()
         {
-            super("SHA256withRSA", NISTObjectIdentifiers.id_sha256, new SHA256Digest(), new PKCS1Encoding(new RSAEngine()));
+            super(NISTObjectIdentifiers.id_sha256, new SHA256Digest(), new PKCS1Encoding(new RSABlindedEngine()));
         }
     }
-    
+
     static public class SHA384WithRSAEncryption
         extends JDKDigestSignature
     {
         public SHA384WithRSAEncryption()
         {
-            super("SHA384withRSA", NISTObjectIdentifiers.id_sha384, new SHA384Digest(), new PKCS1Encoding(new RSAEngine()));
+            super(NISTObjectIdentifiers.id_sha384, new SHA384Digest(), new PKCS1Encoding(new RSABlindedEngine()));
         }
     }
-    
+
     static public class SHA512WithRSAEncryption
         extends JDKDigestSignature
     {
         public SHA512WithRSAEncryption()
         {
-            super("SHA512withRSA", NISTObjectIdentifiers.id_sha512, new SHA512Digest(), new PKCS1Encoding(new RSAEngine()));
+            super(NISTObjectIdentifiers.id_sha512, new SHA512Digest(), new PKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -289,55 +315,64 @@
     // {
     //     public MD2WithRSAEncryption()
     //     {
-    //         super("MD2withRSA", md2, new MD2Digest(), new PKCS1Encoding(new RSAEngine()));
+    //         super(PKCSObjectIdentifiers.md2, new MD2Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class MD4WithRSAEncryption
+    //     extends JDKDigestSignature
+    // {
+    //     public MD4WithRSAEncryption()
+    //     {
+    //         super(PKCSObjectIdentifiers.md4, new MD4Digest(), new PKCS1Encoding(new RSABlindedEngine()));
     //     }
     // }
     // END android-removed
 
-    static public class MD4WithRSAEncryption
-        extends JDKDigestSignature
-    {
-        public MD4WithRSAEncryption()
-        {
-            super("MD4withRSA", md4, new MD4Digest(), new PKCS1Encoding(new RSAEngine()));
-        }
-    }
-
     static public class MD5WithRSAEncryption
         extends JDKDigestSignature
     {
         public MD5WithRSAEncryption()
         {
-            super("MD5withRSA", md5, new MD5Digest(), new PKCS1Encoding(new RSAEngine()));
+            super(PKCSObjectIdentifiers.md5, new MD5Digest(), new PKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
-// BEGIN android-removed
-//    static public class RIPEMD160WithRSAEncryption
-//        extends JDKDigestSignature
-//    {
-//        public RIPEMD160WithRSAEncryption()
-//        {
-//            super("RIPEMD160withRSA", TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new RSAEngine()));
-//        }
-//    }
-//    
-//    static public class RIPEMD128WithRSAEncryption
-//        extends JDKDigestSignature
-//    {
-//        public RIPEMD128WithRSAEncryption()
-//        {
-//            super("RIPEMD128withRSA", TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new RSAEngine()));
-//        }
-//    }
-//    
-//    static public class RIPEMD256WithRSAEncryption
-//        extends JDKDigestSignature
-//    {
-//        public RIPEMD256WithRSAEncryption()
-//        {
-//            super("RIPEMD256withRSA", TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new RSAEngine()));
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // static public class RIPEMD160WithRSAEncryption
+    //     extends JDKDigestSignature
+    // {
+    //     public RIPEMD160WithRSAEncryption()
+    //     {
+    //         super(TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class RIPEMD128WithRSAEncryption
+    //     extends JDKDigestSignature
+    // {
+    //     public RIPEMD128WithRSAEncryption()
+    //     {
+    //         super(TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class RIPEMD256WithRSAEncryption
+    //     extends JDKDigestSignature
+    // {
+    //     public RIPEMD256WithRSAEncryption()
+    //     {
+    //         super(TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    //
+    // static public class noneRSA
+    //     extends JDKDigestSignature
+    // {
+    //     public noneRSA()
+    //     {
+    //         super(new NullDigest(), new PKCS1Encoding(new RSABlindedEngine()));
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKECDSAAlgParameters.java b/src/main/java/org/bouncycastle/jce/provider/JDKECDSAAlgParameters.java
deleted file mode 100644
index 557c705..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/JDKECDSAAlgParameters.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DEROctetString;
-
-import java.io.IOException;
-import java.security.AlgorithmParametersSpi;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-
-public abstract class JDKECDSAAlgParameters
-    extends AlgorithmParametersSpi
-{
-    public static class SigAlgParameters
-        extends JDKAlgorithmParameters
-    {
-        private DEREncodable  params;
-
-        protected byte[] engineGetEncoded() 
-            throws IOException
-        {
-            return engineGetEncoded("ASN.1");
-        }
-
-        protected byte[] engineGetEncoded(
-            String format) 
-            throws IOException
-        {
-            if (format == null)
-            {
-                return engineGetEncoded("ASN.1");
-            }
-            
-            if (format.equals("ASN.1"))
-            {
-                return new DEROctetString(engineGetEncoded("RAW")).getEncoded();
-            }
-
-            return null;
-        }
-
-        protected AlgorithmParameterSpec engineGetParameterSpec(
-            Class paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            throw new InvalidParameterSpecException("unknown parameter spec passed to ECDSA parameters object.");
-        }
-
-        protected void engineInit(
-            AlgorithmParameterSpec paramSpec) 
-            throws InvalidParameterSpecException
-        {
-            throw new InvalidParameterSpecException("unknown parameter spec passed to ECDSA parameters object.");
-        }
-
-        protected void engineInit(
-            byte[] params) 
-            throws IOException
-        {
-        }
-
-        protected void engineInit(
-            byte[] params,
-            String format) 
-            throws IOException
-        {
-            throw new IOException("Unknown parameters format in IV parameters object");
-        }
-
-        protected String engineToString() 
-        {
-            return "ECDSA Parameters";
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKISOSignature.java b/src/main/java/org/bouncycastle/jce/provider/JDKISOSignature.java
deleted file mode 100644
index ec8667d..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/JDKISOSignature.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.security.InvalidKeyException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.digests.MD5Digest;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-// END android-removed
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.engines.RSAEngine;
-import org.bouncycastle.crypto.signers.ISO9796d2Signer;
-
-public class JDKISOSignature
-    extends Signature
-{
-    private ISO9796d2Signer         signer;
-
-    protected JDKISOSignature(
-        String                  name,
-        Digest                  digest,
-        AsymmetricBlockCipher   cipher)
-    {
-        super(name);
-
-        signer = new ISO9796d2Signer(cipher, digest, true);
-    }
-
-    protected void engineInitVerify(
-        PublicKey   publicKey)
-        throws InvalidKeyException
-    {
-        CipherParameters    param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey);
-
-        signer.init(false, param);
-    }
-
-    protected void engineInitSign(
-        PrivateKey  privateKey)
-        throws InvalidKeyException
-    {
-        CipherParameters    param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey);
-
-        signer.init(true, param);
-    }
-
-    protected void engineUpdate(
-        byte    b)
-        throws SignatureException
-    {
-        signer.update(b);
-    }
-
-    protected void engineUpdate(
-        byte[]  b,
-        int     off,
-        int     len) 
-        throws SignatureException
-    {
-        signer.update(b, off, len);
-    }
-
-    protected byte[] engineSign()
-        throws SignatureException
-    {
-        try
-        {
-            byte[]  sig = signer.generateSignature();
-
-            return sig;
-        }
-        catch (Exception e)
-        {
-            throw new SignatureException(e.toString());
-        }
-    }
-
-    protected boolean engineVerify(
-        byte[]  sigBytes) 
-        throws SignatureException
-    {
-        boolean yes = signer.verifySignature(sigBytes);
-
-        return yes;
-    }
-
-    protected void engineSetParameter(
-        AlgorithmParameterSpec params)
-    {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
-    }
-
-    /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
-     */
-    protected void engineSetParameter(
-        String  param,
-        Object  value)
-    {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
-    }
-
-    /**
-     * @deprecated
-     */
-    protected Object engineGetParameter(
-        String      param)
-    {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
-    }
-
-    static public class SHA1WithRSAEncryption
-        extends JDKISOSignature
-    {
-        public SHA1WithRSAEncryption()
-        {
-            super("SHA1withRSA/ISO9796-2", new SHA1Digest(), new RSAEngine());
-        }
-    }
-
-    static public class MD5WithRSAEncryption
-        extends JDKISOSignature
-    {
-        public MD5WithRSAEncryption()
-        {
-            super("MD5withRSA/ISO9796-2", new MD5Digest(), new RSAEngine());
-        }
-    }
-
-// BEGIN android-removed
-//    static public class RIPEMD160WithRSAEncryption
-//        extends JDKISOSignature
-//    {
-//        public RIPEMD160WithRSAEncryption()
-//        {
-//            super("RIPEMD160withRSA/ISO9796-2", new RIPEMD160Digest(), new RSAEngine());
-//        }
-//    }
-// END android-removed
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKKeyFactory.java b/src/main/java/org/bouncycastle/jce/provider/JDKKeyFactory.java
index d5a05df..8ee9a64 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKKeyFactory.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKKeyFactory.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.jce.provider;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.KeyFactorySpi;
@@ -27,7 +26,7 @@
 import javax.crypto.spec.DHPrivateKeySpec;
 import javax.crypto.spec.DHPublicKeySpec;
 
-import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
@@ -40,8 +39,6 @@
 // BEGIN android-removed
 // import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
 // import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
-// import org.bouncycastle.jce.spec.ECPrivateKeySpec;
-// import org.bouncycastle.jce.spec.ECPublicKeySpec;
 // import org.bouncycastle.jce.spec.ElGamalPrivateKeySpec;
 // import org.bouncycastle.jce.spec.ElGamalPublicKeySpec;
 // import org.bouncycastle.jce.spec.GOST3410PrivateKeySpec;
@@ -51,12 +48,54 @@
 public abstract class JDKKeyFactory
     extends KeyFactorySpi
 {
-    protected boolean elGamalFactory = false;
+    // BEGIN android-removed
+    // protected boolean elGamalFactory = false;
+    // END android-removed
     
     public JDKKeyFactory()
     {
     }
 
+    protected PrivateKey engineGeneratePrivate(
+        KeySpec keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof PKCS8EncodedKeySpec)
+        {
+            try
+            {
+                return JDKKeyFactory.createPrivateKeyFromDERStream(
+                    ((PKCS8EncodedKeySpec)keySpec).getEncoded());
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+    }
+
+    protected PublicKey engineGeneratePublic(
+        KeySpec    keySpec)
+        throws InvalidKeySpecException
+    {
+        if (keySpec instanceof X509EncodedKeySpec)
+        {
+            try
+            {
+                return JDKKeyFactory.createPublicKeyFromDERStream(
+                    ((X509EncodedKeySpec)keySpec).getEncoded());
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeySpecException(e.toString());
+            }
+        }
+
+        throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+    }
+    
     protected KeySpec engineGetKeySpec(
         Key    key,
         Class    spec)
@@ -106,7 +145,7 @@
            return new DHPublicKeySpec(k.getY(), k.getParams().getP(), k.getParams().getG());
        }
 
-        throw new RuntimeException("not implemented yet " + key + " " + spec);
+       throw new RuntimeException("not implemented yet " + key + " " + spec);
     }
 
     protected Key engineTranslateKey(
@@ -180,12 +219,12 @@
     /**
      * create a public key from the given DER encoded input stream. 
      */ 
-    static PublicKey createPublicKeyFromDERStream(
+    public static PublicKey createPublicKeyFromDERStream(
         byte[]         in)
         throws IOException
     {
         return createPublicKeyFromPublicKeyInfo(
-                new SubjectPublicKeyInfo((ASN1Sequence)(new ASN1InputStream(in).readObject())));
+            new SubjectPublicKeyInfo((ASN1Sequence) ASN1Object.fromByteArray(in)));
     }
 
     /**
@@ -244,12 +283,12 @@
     /**
      * create a private key from the given DER encoded input stream. 
      */ 
-    static PrivateKey createPrivateKeyFromDERStream(
+    protected static PrivateKey createPrivateKeyFromDERStream(
         byte[]         in)
         throws IOException
     {
         return createPrivateKeyFromPrivateKeyInfo(
-                new PrivateKeyInfo((ASN1Sequence)(new ASN1InputStream(in).readObject())));
+            new PrivateKeyInfo((ASN1Sequence) ASN1Object.fromByteArray(in)));
     }
 
     /**
@@ -325,7 +364,7 @@
                     {
                         return new JCERSAPrivateCrtKey(
                             new RSAPrivateKeyStructure(
-                                (ASN1Sequence)new ASN1InputStream(((PKCS8EncodedKeySpec)keySpec).getEncoded()).readObject()));
+                                (ASN1Sequence) ASN1Object.fromByteArray(((PKCS8EncodedKeySpec)keySpec).getEncoded())));
                     }
                     catch (Exception ex)
                     {
@@ -349,24 +388,12 @@
             KeySpec    keySpec)
             throws InvalidKeySpecException
         {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            else if (keySpec instanceof RSAPublicKeySpec)
+            if (keySpec instanceof RSAPublicKeySpec)
             {
                 return new JCERSAPublicKey((RSAPublicKeySpec)keySpec);
             }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+
+            return super.engineGeneratePublic(keySpec);
         }
     }
 
@@ -381,48 +408,24 @@
             KeySpec    keySpec)
             throws InvalidKeySpecException
         {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                                ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            else if (keySpec instanceof DHPrivateKeySpec)
+            if (keySpec instanceof DHPrivateKeySpec)
             {
                 return new JCEDHPrivateKey((DHPrivateKeySpec)keySpec);
             }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+
+            return super.engineGeneratePrivate(keySpec);
         }
     
         protected PublicKey engineGeneratePublic(
             KeySpec    keySpec)
             throws InvalidKeySpecException
         {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            else if (keySpec instanceof DHPublicKeySpec)
+            if (keySpec instanceof DHPublicKeySpec)
             {
                 return new JCEDHPublicKey((DHPublicKeySpec)keySpec);
             }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+
+            return super.engineGeneratePublic(keySpec);
         }
     }
 
@@ -437,343 +440,113 @@
             KeySpec    keySpec)
             throws InvalidKeySpecException
         {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                                ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            else if (keySpec instanceof DSAPrivateKeySpec)
+            if (keySpec instanceof DSAPrivateKeySpec)
             {
                 return new JDKDSAPrivateKey((DSAPrivateKeySpec)keySpec);
             }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+
+            return super.engineGeneratePrivate(keySpec);
         }
     
         protected PublicKey engineGeneratePublic(
             KeySpec    keySpec)
             throws InvalidKeySpecException
         {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            else if (keySpec instanceof DSAPublicKeySpec)
+            if (keySpec instanceof DSAPublicKeySpec)
             {
                 return new JDKDSAPublicKey((DSAPublicKeySpec)keySpec);
             }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
+
+            return super.engineGeneratePublic(keySpec);
         }
     }
 
-    public static class GOST3410
-        extends JDKKeyFactory
-    {
-        public GOST3410()
-        {
-        }
-        
-        protected PrivateKey engineGeneratePrivate(
-                KeySpec    keySpec)
-        throws InvalidKeySpecException
-        {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                            ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof GOST3410PrivateKeySpec)
-            // {
-            //     return new JDKGOST3410PrivateKey((GOST3410PrivateKeySpec)keySpec);
-            // }
-            // END android-removed
-            
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-        
-        protected PublicKey engineGeneratePublic(
-                KeySpec    keySpec)
-        throws InvalidKeySpecException
-        {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                            ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof GOST3410PublicKeySpec)
-            // {
-            //     return new JDKGOST3410PublicKey((GOST3410PublicKeySpec)keySpec);
-            // }
-            // END android-removed
-            
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    }
+    // BEGIN android-removed
+    // public static class GOST3410
+    //     extends JDKKeyFactory
+    // {
+    //     public GOST3410()
+    //     {
+    //     }
+    //
+    //     protected PrivateKey engineGeneratePrivate(
+    //             KeySpec    keySpec)
+    //     throws InvalidKeySpecException
+    //     {
+    //         if (keySpec instanceof GOST3410PrivateKeySpec)
+    //         {
+    //             return new JDKGOST3410PrivateKey((GOST3410PrivateKeySpec)keySpec);
+    //         }
+    //
+    //         return super.engineGeneratePrivate(keySpec);
+    //     }
+    //
+    //     protected PublicKey engineGeneratePublic(
+    //             KeySpec    keySpec)
+    //     throws InvalidKeySpecException
+    //     {
+    //         if (keySpec instanceof GOST3410PublicKeySpec)
+    //         {
+    //             return new JDKGOST3410PublicKey((GOST3410PublicKeySpec)keySpec);
+    //         }
+    //
+    //         return super.engineGeneratePublic(keySpec);
+    //     }
+    // }
     
-    public static class ElGamal
-        extends JDKKeyFactory
-    {
-        public ElGamal()
-        {
-            elGamalFactory = true;
-        }
-
-        protected PrivateKey engineGeneratePrivate(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                                ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof ElGamalPrivateKeySpec)
-            // {
-            //     return new JCEElGamalPrivateKey((ElGamalPrivateKeySpec)keySpec);
-            // }
-            // else if (keySpec instanceof DHPrivateKeySpec)
-            // {
-            //     return new JCEElGamalPrivateKey((DHPrivateKeySpec)keySpec);
-            // }
-            // END android-removed
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    
-        protected PublicKey engineGeneratePublic(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof ElGamalPublicKeySpec)
-            // {
-            //     return new JCEElGamalPublicKey((ElGamalPublicKeySpec)keySpec);
-            // }
-            // else if (keySpec instanceof DHPublicKeySpec)
-            // {
-            //     return new JCEElGamalPublicKey((DHPublicKeySpec)keySpec);
-            // }
-            // END android-removed
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    }
-
-
-    /**
-     * This isn't really correct, however the class path project API seems to think such
-     * a key factory will exist.
-     */
-    public static class X509
-        extends JDKKeyFactory
-    {
-        public X509()
-        {
-        }
-    
-        protected PrivateKey engineGeneratePrivate(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                                ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    
-        protected PublicKey engineGeneratePublic(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    }
-    
-    public static class EC
-        extends JDKKeyFactory
-    {
-        String  algorithm;
-
-        public EC()
-        {
-            this("EC");
-        }
-
-        public EC(
-            String  algorithm)
-        {
-            this.algorithm = algorithm;
-        }
-
-        protected PrivateKey engineGeneratePrivate(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof PKCS8EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPrivateKeyFromDERStream(
-                                ((PKCS8EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof ECPrivateKeySpec)
-            // {
-            //     return new JCEECPrivateKey(algorithm, (ECPrivateKeySpec)keySpec);
-            // }
-            // else if (keySpec instanceof java.security.spec.ECPrivateKeySpec)
-            // {
-            //     return new JCEECPrivateKey(algorithm, (java.security.spec.ECPrivateKeySpec)keySpec);
-            // }
-            // END android-removed
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    
-        protected PublicKey engineGeneratePublic(
-            KeySpec    keySpec)
-            throws InvalidKeySpecException
-        {
-            if (keySpec instanceof X509EncodedKeySpec)
-            {
-                try
-                {
-                    return JDKKeyFactory.createPublicKeyFromDERStream(
-                                ((X509EncodedKeySpec)keySpec).getEncoded());
-                }
-                catch (Exception e)
-                {
-                    throw new InvalidKeySpecException(e.toString());
-                }
-            }
-            // BEGIN android-removed
-            // else if (keySpec instanceof ECPublicKeySpec)
-            // {
-            //     return new JCEECPublicKey(algorithm, (ECPublicKeySpec)keySpec);
-            // }
-            // else if (keySpec instanceof java.security.spec.ECPublicKeySpec)
-            // {
-            //     return new JCEECPublicKey(algorithm, (java.security.spec.ECPublicKeySpec)keySpec);
-            // }
-            // END android-removed
-    
-            throw new InvalidKeySpecException("Unknown KeySpec type: " + keySpec.getClass().getName());
-        }
-    }
-
-    public static class ECDSA
-        extends EC
-    {
-        public ECDSA()
-        {
-            super("ECDSA");
-        }
-    }
-
-    public static class ECGOST3410
-        extends EC
-    {
-        public ECGOST3410()
-        {
-            super("ECGOST3410");
-        }
-    }
-    
-    public static class ECDH
-        extends EC
-    {
-        public ECDH()
-        {
-            super("ECDH");
-        }
-    }
-
-    public static class ECDHC
-        extends EC
-    {
-        public ECDHC()
-        {
-            super("ECDHC");
-        }
-    }
+    // public static class ElGamal
+    //     extends JDKKeyFactory
+    // {
+    //     public ElGamal()
+    //     {
+    //         elGamalFactory = true;
+    //     }
+    //
+    //     protected PrivateKey engineGeneratePrivate(
+    //         KeySpec    keySpec)
+    //         throws InvalidKeySpecException
+    //     {
+    //         if (keySpec instanceof ElGamalPrivateKeySpec)
+    //         {
+    //             return new JCEElGamalPrivateKey((ElGamalPrivateKeySpec)keySpec);
+    //         }
+    //         else if (keySpec instanceof DHPrivateKeySpec)
+    //         {
+    //             return new JCEElGamalPrivateKey((DHPrivateKeySpec)keySpec);
+    //         }
+    //
+    //         return super.engineGeneratePrivate(keySpec);
+    //     }
+    //
+    //     protected PublicKey engineGeneratePublic(
+    //         KeySpec    keySpec)
+    //         throws InvalidKeySpecException
+    //     {
+    //         if (keySpec instanceof ElGamalPublicKeySpec)
+    //         {
+    //             return new JCEElGamalPublicKey((ElGamalPublicKeySpec)keySpec);
+    //         }
+    //         else if (keySpec instanceof DHPublicKeySpec)
+    //         {
+    //             return new JCEElGamalPublicKey((DHPublicKeySpec)keySpec);
+    //         }
+    //
+    //         return super.engineGeneratePublic(keySpec);
+    //     }
+    // }
+    //
+    //
+    //
+    // /**
+    //  * This isn't really correct, however the class path project API seems to think such
+    //  * a key factory will exist.
+    //  */
+    // public static class X509
+    //     extends JDKKeyFactory
+    // {
+    //     public X509()
+    //     {
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java b/src/main/java/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java
index 24233d6..e0f4246 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKKeyPairGenerator.java
@@ -1,5 +1,44 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
+import org.bouncycastle.crypto.generators.DHParametersGenerator;
+import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
+import org.bouncycastle.crypto.generators.DSAParametersGenerator;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
+// import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
+// import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
+// END android-removed
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
+// import org.bouncycastle.crypto.params.ElGamalParameters;
+// import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+// import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+// import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
+// import org.bouncycastle.crypto.params.GOST3410Parameters;
+// import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
+// import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
+// END android-removed
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
+// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
+// END android-removed
+
 import java.math.BigInteger;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidParameterException;
@@ -8,48 +47,11 @@
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.DSAParameterSpec;
-import java.security.spec.ECField;
-import java.security.spec.ECFieldF2m;
-import java.security.spec.ECFieldFp;
-import java.security.spec.ECGenParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
 import java.util.Hashtable;
 
 import javax.crypto.spec.DHParameterSpec;
 
-import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-// BEGIN android-removed
-// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
-// import org.bouncycastle.asn1.nist.NISTNamedCurves;
-// import org.bouncycastle.asn1.sec.SECNamedCurves;
-// import org.bouncycastle.asn1.x9.X962NamedCurves;
-// import org.bouncycastle.asn1.x9.X9ECParameters;
-// END android-removed
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
-import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
-import org.bouncycastle.crypto.generators.DHParametersGenerator;
-import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
-import org.bouncycastle.crypto.generators.DSAParametersGenerator;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
-// import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
-// import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
-// import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
-// END android-removed
-import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
-import org.bouncycastle.crypto.params.*;
-// BEGIN android-removed
-// import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
-// import org.bouncycastle.jce.spec.ECNamedCurveSpec;
-// import org.bouncycastle.jce.spec.ECParameterSpec;
-// import org.bouncycastle.jce.spec.ElGamalParameterSpec;
-// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
-// import org.bouncycastle.jce.spec.GOST3410PublicKeyParameterSetSpec;
-// import org.bouncycastle.math.ec.ECCurve;
-// import org.bouncycastle.math.ec.ECFieldElement;
-// import org.bouncycastle.math.ec.ECPoint;
-// END android-removed
-
 public abstract class JDKKeyPairGenerator
     extends KeyPairGenerator
 {
@@ -67,7 +69,7 @@
         extends JDKKeyPairGenerator
     {
         final static BigInteger defaultPublicExponent = BigInteger.valueOf(0x10001);
-        final static int defaultTests = 8;
+        final static int defaultTests = 12;
 
         RSAKeyGenerationParameters  param;
         RSAKeyPairGenerator         engine;
@@ -124,6 +126,8 @@
     public static class DH
         extends JDKKeyPairGenerator
     {
+        private static Hashtable   params = new Hashtable();
+
         DHKeyGenerationParameters  param;
         DHBasicKeyPairGenerator    engine = new DHBasicKeyPairGenerator();
         int                        strength = 1024;
@@ -155,7 +159,7 @@
             }
             DHParameterSpec     dhParams = (DHParameterSpec)params;
 
-            param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG()));
+            param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
 
             engine.init(param);
             initialised = true;
@@ -165,17 +169,33 @@
         {
             if (!initialised)
             {
-                DHParametersGenerator   pGen = new DHParametersGenerator();
+                // BEGIN android-changed
+                Integer paramStrength = Integer.valueOf(strength);
+                // END android-changed
 
-                pGen.init(strength, certainty, random);
-                param = new DHKeyGenerationParameters(random, pGen.generateParameters());
+                if (params.containsKey(paramStrength))
+                {
+                    param = (DHKeyGenerationParameters)params.get(paramStrength);
+                }
+                else
+                {
+                    DHParametersGenerator   pGen = new DHParametersGenerator();
+
+                    pGen.init(strength, certainty, random);
+
+                    param = new DHKeyGenerationParameters(random, pGen.generateParameters());
+
+                    params.put(paramStrength, param);
+                }
+
                 engine.init(param);
+
                 initialised = true;
             }
 
-            AsymmetricCipherKeyPair   pair = engine.generateKeyPair();
-            DHPublicKeyParameters     pub = (DHPublicKeyParameters)pair.getPublic();
-            DHPrivateKeyParameters priv = (DHPrivateKeyParameters)pair.getPrivate();
+            AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+            DHPublicKeyParameters   pub = (DHPublicKeyParameters)pair.getPublic();
+            DHPrivateKeyParameters  priv = (DHPrivateKeyParameters)pair.getPrivate();
 
             return new KeyPair(new JCEDHPublicKey(pub),
                                new JCEDHPrivateKey(priv));
@@ -201,6 +221,11 @@
             int             strength,
             SecureRandom    random)
         {
+            if (strength < 512 || strength > 1024 || strength % 64 != 0)
+            {
+                throw new InvalidParameterException("strength must be from 512 - 1024 and a multiple of 64");
+            }
+
             this.strength = strength;
             this.random = random;
         }
@@ -243,379 +268,143 @@
         }
     }
 
-// BEGIN android-removed
-//    public static class ElGamal
-//        extends JDKKeyPairGenerator
-//    {
-//        ElGamalKeyGenerationParameters  param;
-//        ElGamalKeyPairGenerator         engine = new ElGamalKeyPairGenerator();
-//        int                             strength = 1024;
-//        int                             certainty = 20;
-//        SecureRandom                    random = new SecureRandom();
-//        boolean                         initialised = false;
-//
-//        public ElGamal()
-//        {
-//            super("ElGamal");
-//        }
-//
-//        public void initialize(
-//            int             strength,
-//            SecureRandom    random)
-//        {
-//            this.strength = strength;
-//            this.random = random;
-//        }
-//
-//        public void initialize(
-//            AlgorithmParameterSpec  params,
-//            SecureRandom            random)
-//            throws InvalidAlgorithmParameterException
-//        {
-//            if (!(params instanceof ElGamalParameterSpec) && !(params instanceof DHParameterSpec))
-//            {
-//                throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec or an ElGamalParameterSpec");
-//            }
-//            
-//            if (params instanceof ElGamalParameterSpec)
-//            {
-//                ElGamalParameterSpec     elParams = (ElGamalParameterSpec)params;
-//
-//                param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(elParams.getP(), elParams.getG()));
-//            }
-//            else
-//            {
-//                DHParameterSpec     dhParams = (DHParameterSpec)params;
-//
-//                param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG()));
-//            }
-//
-//            engine.init(param);
-//            initialised = true;
-//        }
-//
-//        public KeyPair generateKeyPair()
-//        {
-//            if (!initialised)
-//            {
-//                ElGamalParametersGenerator   pGen = new ElGamalParametersGenerator();
-//
-//                pGen.init(strength, certainty, random);
-//                param = new ElGamalKeyGenerationParameters(random, pGen.generateParameters());
-//                engine.init(param);
-//                initialised = true;
-//            }
-//
-//            AsymmetricCipherKeyPair         pair = engine.generateKeyPair();
-//            ElGamalPublicKeyParameters      pub = (ElGamalPublicKeyParameters)pair.getPublic();
-//            ElGamalPrivateKeyParameters     priv = (ElGamalPrivateKeyParameters)pair.getPrivate();
-//
-//            return new KeyPair(new JCEElGamalPublicKey(pub),
-//                               new JCEElGamalPrivateKey(priv));
-//        }
-//    }
-//
-//    public static class GOST3410
-//        extends JDKKeyPairGenerator
-//    {
-//        GOST3410KeyGenerationParameters param;
-//        GOST3410KeyPairGenerator        engine = new GOST3410KeyPairGenerator();
-//        GOST3410ParameterSpec           gost3410Params;
-//        int                             strength = 1024;
-//        SecureRandom                    random = null;
-//        boolean                         initialised = false;
-//
-//        public GOST3410()
-//        {
-//            super("GOST3410");
-//        }
-//
-//        public void initialize(
-//            int             strength,
-//            SecureRandom    random)
-//        {
-//            this.strength = strength;
-//            this.random = random;
-//        }
-//    
-//        private void init(
-//            GOST3410ParameterSpec gParams,
-//            SecureRandom          random)
-//        {
-//            GOST3410PublicKeyParameterSetSpec spec = gParams.getPublicKeyParameters();
-//            
-//            param = new GOST3410KeyGenerationParameters(random, new GOST3410Parameters(spec.getP(), spec.getQ(), spec.getA()));
-//            
-//            engine.init(param);
-//            
-//            initialised = true;
-//            gost3410Params = gParams;
-//        }
-//        
-//        public void initialize(
-//            AlgorithmParameterSpec  params,
-//            SecureRandom            random)
-//            throws InvalidAlgorithmParameterException
-//        {
-//            if (!(params instanceof GOST3410ParameterSpec))
-//            {
-//                throw new InvalidAlgorithmParameterException("parameter object not a GOST3410ParameterSpec");
-//            }
-//            
-//            init((GOST3410ParameterSpec)params, random);
-//        }
-//
-//        public KeyPair generateKeyPair()
-//        {
-//            if (!initialised)
-//            {
-//                init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom());
-//            }
-//            
-//            AsymmetricCipherKeyPair   pair = engine.generateKeyPair();
-//            GOST3410PublicKeyParameters  pub = (GOST3410PublicKeyParameters)pair.getPublic();
-//            GOST3410PrivateKeyParameters priv = (GOST3410PrivateKeyParameters)pair.getPrivate();
-//            
-//            return new KeyPair(new JDKGOST3410PublicKey(pub, gost3410Params), new JDKGOST3410PrivateKey(priv, gost3410Params));
-//        }
-//   }
-//    
-//    public static class EC
-//        extends JDKKeyPairGenerator
-//    {
-//        ECKeyGenerationParameters   param;
-//        ECKeyPairGenerator          engine = new ECKeyPairGenerator();
-//        Object                      ecParams = null;
-//        int                         strength = 239;
-//        int                         certainty = 50;
-//        SecureRandom                random = new SecureRandom();
-//        boolean                     initialised = false;
-//        String                      algorithm;
-//
-//        static private Hashtable    ecParameters;
-//
-//        static {
-//            ecParameters = new Hashtable();
-//
-//            ecParameters.put(Integer.valueOf(192), new ECGenParameterSpec("prime192v1"));
-//            ecParameters.put(Integer.valueOf(239), new ECGenParameterSpec("prime239v1"));
-//            ecParameters.put(Integer.valueOf(256), new ECGenParameterSpec("prime256v1"));
-//        }
-//
-//        public EC()
-//        {
-//            super("EC");
-//            this.algorithm = "EC";
-//        }
-//        
-//        public EC(
-//            String  algorithm)
-//        {
-//            super(algorithm);
-//            this.algorithm = algorithm;
-//        }
-//
-//        public void initialize(
-//            int             strength,
-//            SecureRandom    random)
-//        {
-//            this.strength = strength;
-//            this.random = random;
-//            this.ecParams = (ECGenParameterSpec)ecParameters.get(Integer.valueOf(strength));
-//
-//            if (ecParams != null)
-//            {
-//                try
-//                {
-//                    initialize((ECGenParameterSpec)ecParams, random);
-//                }
-//                catch (InvalidAlgorithmParameterException e)
-//                {
-//                    throw new InvalidParameterException("key size not configurable.");
-//                }
-//            }
-//            else
-//            {
-//                throw new InvalidParameterException("unknown key size.");
-//            }
-//        }
-//
-//        public void initialize(
-//            AlgorithmParameterSpec  params,
-//            SecureRandom            random)
-//            throws InvalidAlgorithmParameterException
-//        {
-//            if (params instanceof ECParameterSpec)
-//            {
-//                ECParameterSpec p = (ECParameterSpec)params;
-//                this.ecParams = params;
-//    
-//                param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random);
-//    
-//                engine.init(param);
-//                initialised = true;
-//            }
-//            else if (params instanceof java.security.spec.ECParameterSpec)
-//            {
-//                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)params;
-//                this.ecParams = params;
-//
-//                ECCurve curve;
-//                ECPoint g;
-//                ECField field = p.getCurve().getField();
-//
-//                if (field instanceof ECFieldFp)
-//                {
-//                    curve = new ECCurve.Fp(((ECFieldFp)p.getCurve().getField()).getP(), p.getCurve().getA(), p.getCurve().getB());
-//                    g = new ECPoint.Fp(curve, new ECFieldElement.Fp(((ECCurve.Fp)curve).getQ(), p.getGenerator().getAffineX()), new ECFieldElement.Fp(((ECCurve.Fp)curve).getQ(), p.getGenerator().getAffineY()));
-//                }
-//                else
-//                {
-//                    ECFieldF2m fieldF2m = (ECFieldF2m)field;
-//                    int m = fieldF2m.getM();
-//                    int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial());
-//                    curve = new ECCurve.F2m(m, ks[0], ks[1], ks[2], p.getCurve().getA(), p.getCurve().getB());
-//                    g = new ECPoint.F2m(curve, new ECFieldElement.F2m(m, ks[0], ks[1], ks[2], p.getGenerator().getAffineX()), new ECFieldElement.F2m(m, ks[0], ks[1], ks[2], p.getGenerator().getAffineY()), false);
-//                }
-//                param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
-//    
-//                engine.init(param);
-//                initialised = true;
-//            }
-//            else if (params instanceof ECGenParameterSpec)
-//            {
-//                if (this.algorithm.equals("ECGOST3410"))
-//                {
-//                    ECDomainParameters  ecP = ECGOST3410NamedCurves.getByName(((ECGenParameterSpec)params).getName());
-//                    if (ecP == null)
-//                    {
-//                        throw new InvalidAlgorithmParameterException("unknown curve name: " + ((ECGenParameterSpec)params).getName());
-//                    }
-//
-//                    this.ecParams = new ECNamedCurveParameterSpec(
-//                                                    ((ECGenParameterSpec)params).getName(),
-//                                                    ecP.getCurve(),
-//                                                    ecP.getG(),
-//                                                    ecP.getN(),
-//                                                    ecP.getH(),
-//                                                    ecP.getSeed());
-//                }
-//                else
-//                {
-//                    X9ECParameters  ecP = X962NamedCurves.getByName(((ECGenParameterSpec)params).getName());
-//                    if (ecP == null)
-//                    {
-//                        ecP = SECNamedCurves.getByName(((ECGenParameterSpec)params).getName());
-//                        if (ecP == null)
-//                        {
-//                            ecP = NISTNamedCurves.getByName(((ECGenParameterSpec)params).getName());
-//                        }
-//                        if (ecP == null)
-//                        {
-//                            throw new InvalidAlgorithmParameterException("unknown curve name: " + ((ECGenParameterSpec)params).getName());
-//                        }
-//                    }
-//
-//                    this.ecParams = new ECNamedCurveSpec(
-//                            ((ECGenParameterSpec)params).getName(),
-//                            ecP.getCurve(),
-//                            ecP.getG(),
-//                            ecP.getN(),
-//                            ecP.getH(),
-//                            ecP.getSeed());
-//                }
-//
-//                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
-//                ECCurve curve;
-//                ECPoint g;
-//                ECField field = p.getCurve().getField();
-//
-//                if (field instanceof ECFieldFp)
-//                {
-//                    curve = new ECCurve.Fp(((ECFieldFp)p.getCurve().getField()).getP(), p.getCurve().getA(), p.getCurve().getB());
-//                    g = new ECPoint.Fp(curve, new ECFieldElement.Fp(((ECCurve.Fp)curve).getQ(), p.getGenerator().getAffineX()), new ECFieldElement.Fp(((ECCurve.Fp)curve).getQ(), p.getGenerator().getAffineY()));
-//                }
-//                else
-//                {
-//                    ECFieldF2m fieldF2m = (ECFieldF2m)field;
-//                    int m = fieldF2m.getM();
-//                    int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial());
-//                    curve = new ECCurve.F2m(m, ks[0], ks[1], ks[2], p.getCurve().getA(), p.getCurve().getB());
-//                    g = new ECPoint.F2m(curve, new ECFieldElement.F2m(m, ks[0], ks[1], ks[2], p.getGenerator().getAffineX()), new ECFieldElement.F2m(m, ks[0], ks[1], ks[2], p.getGenerator().getAffineY()), false);
-//                }
-//
-//                param = new ECKeyGenerationParameters(new ECDomainParameters(curve, g, p.getOrder(), BigInteger.valueOf(p.getCofactor())), random);
-//
-//                engine.init(param);
-//                initialised = true;
-//            }
-//            else
-//            {
-//                throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec");
-//            } 
-//        }
-//
-//        public KeyPair generateKeyPair()
-//        {
-//            if (!initialised)
-//            {
-//                throw new IllegalStateException("EC Key Pair Generator not initialised");
-//            }
-//
-//            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
-//            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
-//            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();
-//
-//            if (ecParams instanceof ECParameterSpec)
-//            {
-//                ECParameterSpec p = (ECParameterSpec)ecParams;
-//                
-//                return new KeyPair(new JCEECPublicKey(algorithm, pub, p),
-//                                   new JCEECPrivateKey(algorithm, priv, p));
-//            }
-//            else
-//            {
-//                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
-//                
-//                return new KeyPair(new JCEECPublicKey(algorithm, pub, p), new JCEECPrivateKey(algorithm, priv, p));
-//            }
-//        }
-//    }
-//
-//    public static class ECDSA
-//        extends EC
-//    {
-//        public ECDSA()
-//        {
-//            super("ECDSA");
-//        }
-//    }
-//
-//    public static class ECGOST3410
-//        extends EC
-//    {
-//        public ECGOST3410()
-//        {
-//            super("ECGOST3410");
-//        }
-//    }
-//    
-//    public static class ECDH
-//        extends EC
-//    {
-//        public ECDH()
-//        {
-//            super("ECDH");
-//        }
-//    }
-//
-//    public static class ECDHC
-//        extends EC
-//    {
-//        public ECDHC()
-//        {
-//            super("ECDHC");
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // public static class ElGamal
+    //     extends JDKKeyPairGenerator
+    // {
+    //     ElGamalKeyGenerationParameters  param;
+    //     ElGamalKeyPairGenerator         engine = new ElGamalKeyPairGenerator();
+    //     int                             strength = 1024;
+    //     int                             certainty = 20;
+    //     SecureRandom                    random = new SecureRandom();
+    //     boolean                         initialised = false;
+    //
+    //     public ElGamal()
+    //     {
+    //         super("ElGamal");
+    //     }
+    //
+    //     public void initialize(
+    //         int             strength,
+    //         SecureRandom    random)
+    //     {
+    //         this.strength = strength;
+    //         this.random = random;
+    //     }
+    //
+    //     public void initialize(
+    //         AlgorithmParameterSpec  params,
+    //         SecureRandom            random)
+    //         throws InvalidAlgorithmParameterException
+    //     {
+    //         if (!(params instanceof ElGamalParameterSpec) && !(params instanceof DHParameterSpec))
+    //         {
+    //             throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec or an ElGamalParameterSpec");
+    //         }
+    //
+    //         if (params instanceof ElGamalParameterSpec)
+    //         {
+    //             ElGamalParameterSpec     elParams = (ElGamalParameterSpec)params;
+
+    //             param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(elParams.getP(), elParams.getG()));
+    //         }
+    //         else
+    //         {
+    //             DHParameterSpec     dhParams = (DHParameterSpec)params;
+    //
+    //             param = new ElGamalKeyGenerationParameters(random, new ElGamalParameters(dhParams.getP(), dhParams.getG(), dhParams.getL()));
+    //         }
+    //
+    //         engine.init(param);
+    //         initialised = true;
+    //     }
+    //
+    //     public KeyPair generateKeyPair()
+    //     {
+    //         if (!initialised)
+    //         {
+    //             ElGamalParametersGenerator   pGen = new ElGamalParametersGenerator();
+    //
+    //             pGen.init(strength, certainty, random);
+    //             param = new ElGamalKeyGenerationParameters(random, pGen.generateParameters());
+    //             engine.init(param);
+    //             initialised = true;
+    //         }
+    //
+    //         AsymmetricCipherKeyPair         pair = engine.generateKeyPair();
+    //         ElGamalPublicKeyParameters      pub = (ElGamalPublicKeyParameters)pair.getPublic();
+    //         ElGamalPrivateKeyParameters     priv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+    //
+    //         return new KeyPair(new JCEElGamalPublicKey(pub),
+    //                            new JCEElGamalPrivateKey(priv));
+    //     }
+    // }
+    // END android-removed
+
+   // BEGIN android-removed
+   //  public static class GOST3410
+   //      extends JDKKeyPairGenerator
+   //  {
+   //      GOST3410KeyGenerationParameters param;
+   //      GOST3410KeyPairGenerator        engine = new GOST3410KeyPairGenerator();
+   //      GOST3410ParameterSpec           gost3410Params;
+   //      int                             strength = 1024;
+   //      SecureRandom                    random = null;
+   //      boolean                         initialised = false;
+   //
+   //      public GOST3410()
+   //      {
+   //          super("GOST3410");
+   //      }
+   //
+   //      public void initialize(
+   //          int             strength,
+   //          SecureRandom    random)
+   //      {
+   //          this.strength = strength;
+   //          this.random = random;
+   //      }
+   //
+   //      private void init(
+   //          GOST3410ParameterSpec gParams,
+   //          SecureRandom          random)
+   //      {
+   //          GOST3410PublicKeyParameterSetSpec spec = gParams.getPublicKeyParameters();
+   //
+   //          param = new GOST3410KeyGenerationParameters(random, new GOST3410Parameters(spec.getP(), spec.getQ(), spec.getA()));
+   //
+   //          engine.init(param);
+   //
+   //          initialised = true;
+   //          gost3410Params = gParams;
+   //      }
+   //
+   //      public void initialize(
+   //          AlgorithmParameterSpec  params,
+   //          SecureRandom            random)
+   //          throws InvalidAlgorithmParameterException
+   //      {
+   //          if (!(params instanceof GOST3410ParameterSpec))
+   //          {
+   //              throw new InvalidAlgorithmParameterException("parameter object not a GOST3410ParameterSpec");
+   //          }
+   //
+   //          init((GOST3410ParameterSpec)params, random);
+   //      }
+   //
+   //      public KeyPair generateKeyPair()
+   //      {
+   //          if (!initialised)
+   //          {
+   //              init(new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId()), new SecureRandom());
+   //          }
+   //         
+   //          AsymmetricCipherKeyPair   pair = engine.generateKeyPair();
+   //          GOST3410PublicKeyParameters  pub = (GOST3410PublicKeyParameters)pair.getPublic();
+   //          GOST3410PrivateKeyParameters priv = (GOST3410PrivateKeyParameters)pair.getPrivate();
+   //
+   //          return new KeyPair(new JDKGOST3410PublicKey(pub, gost3410Params), new JDKGOST3410PrivateKey(priv, gost3410Params));
+   //      }
+   // }
+   // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java b/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java
index 0b1002a..8c0b90f 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java
@@ -7,7 +7,16 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.*;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
@@ -27,12 +36,12 @@
 import javax.crypto.spec.PBEParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
-// BEGIN android-added
-import org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigest;
-
-// END android-added
+import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.PBEParametersGenerator;
+// BEGIN android-added
+import org.bouncycastle.crypto.digests.OpenSSLDigest;
+// END android-added
 // BEGIN android-removed
 // import org.bouncycastle.crypto.digests.SHA1Digest;
 // END android-removed
@@ -43,6 +52,8 @@
 import org.bouncycastle.crypto.io.MacOutputStream;
 import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.jce.interfaces.BCKeyStore;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
 
 public class JDKKeyStore
     extends KeyStoreSpi
@@ -102,17 +113,6 @@
 
         StoreEntry(
             String          alias,
-            Key             obj,
-            Certificate[]   certChain)
-        {
-            this.type = KEY;
-            this.alias = alias;
-            this.obj = obj;
-            this.certChain = certChain;
-        }
-
-        StoreEntry(
-            String          alias,
             byte[]          obj,
             Certificate[]   certChain)
         {
@@ -639,7 +639,7 @@
 
         if (entry != null && entry.getType() != CERTIFICATE)
         {
-            throw new KeyStoreException("key store already has an entry with alias " + alias);
+            throw new KeyStoreException("key store already has a key entry with alias " + alias);
         }
 
         table.put(alias, new StoreEntry(alias, cert));
@@ -651,13 +651,6 @@
         Certificate[] chain) 
         throws KeyStoreException
     {
-        StoreEntry  entry = (StoreEntry)table.get(alias);
-
-        if (entry != null)
-        {
-            throw new KeyStoreException("key store already has an entry with alias " + alias);
-        }
-
         table.put(alias, new StoreEntry(alias, key, chain));
     }
 
@@ -673,13 +666,6 @@
             throw new KeyStoreException("no certificate chain for private key");
         }
 
-        StoreEntry  entry = (StoreEntry)table.get(alias);
-
-        if (entry != null && entry.getType() == CERTIFICATE)
-        {
-            throw new KeyStoreException("key store already has an entry with alias " + alias);
-        }
-
         try
         {
             table.put(alias, new StoreEntry(alias, key, password, chain));
@@ -695,26 +681,6 @@
         return table.size();
     }
 
-    protected boolean isSameAs(
-        byte[]  one,
-        byte[]  two)
-    {
-        if (one.length != two.length)
-        {
-            return false;
-        }
-
-        for (int i = 0; i != one.length; i++)
-        {
-            if (one[i] != two[i])
-            {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
     protected void loadStore(
         InputStream in)
         throws IOException
@@ -846,46 +812,51 @@
 
         int         iterationCount = dIn.readInt();
 
-// BEGIN android-removed
-//         HMac                    hMac = new HMac(new SHA1Digest());
-//         MacInputStream          mIn = new MacInputStream(dIn, hMac);
-//         PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
-// END android-removed
-// BEGIN android-added
-        HMac                    hMac = new HMac(OpenSSLMessageDigest.getInstance("SHA-1"));        
-        MacInputStream          mIn = new MacInputStream(dIn, hMac);
-        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(OpenSSLMessageDigest.getInstance("SHA-1"));
-// END android-added
-        byte[]                  passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
-
-        pbeGen.init(passKey, salt, iterationCount);
-
-        hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize()));
-
-        for (int i = 0; i != passKey.length; i++)
-        {
-            passKey[i] = 0;
-        }
-
-        loadStore(mIn);
-
-        byte[]  mac = new byte[hMac.getMacSize()];
-        byte[]  oldMac = new byte[hMac.getMacSize()];
-
-        hMac.doFinal(mac, 0);
-
-        for (int i = 0; i != oldMac.length; i++)
-        {
-            oldMac[i] = (byte)dIn.read();
-        }
-
         //
         // we only do an integrity check if the password is provided.
         //
-        if ((password != null && password.length != 0) && !isSameAs(mac, oldMac))
+        // BEGIN android-changed
+        HMac hMac = new HMac(new OpenSSLDigest.SHA1());
+        // END android-changed
+        if (password != null && password.length != 0)
         {
-            table.clear();
-            throw new IOException("KeyStore integrity check failed.");
+            byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
+
+            // BEGIN android-changed
+            PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(new OpenSSLDigest.SHA1());
+            // END android-changed
+            pbeGen.init(passKey, salt, iterationCount);
+            CipherParameters macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize());
+            Arrays.fill(passKey, (byte)0);
+
+            hMac.init(macParams);
+            MacInputStream mIn = new MacInputStream(dIn, hMac);
+
+            loadStore(mIn);
+
+            // Finalise our mac calculation
+            byte[] mac = new byte[hMac.getMacSize()];
+            hMac.doFinal(mac, 0);
+
+            // TODO Should this actually be reading the remainder of the stream?
+            // Read the original mac from the stream
+            byte[] oldMac = new byte[hMac.getMacSize()];
+            dIn.readFully(oldMac);
+
+            if (!Arrays.constantTimeAreEqual(mac, oldMac))
+            {
+                table.clear();
+                throw new IOException("KeyStore integrity check failed.");
+            }
+        }
+        else
+        {
+            loadStore(dIn);
+
+            // TODO Should this actually be reading the remainder of the stream?
+            // Parse the original mac from the stream too
+            byte[] oldMac = new byte[hMac.getMacSize()];
+            dIn.readFully(oldMac);
         }
     }
 
@@ -904,16 +875,11 @@
         dOut.write(salt);
         dOut.writeInt(iterationCount);
 
-// BEGIN android-removed
-//        HMac                    hMac = new HMac(new SHA1Digest());
-//        MacOutputStream         mOut = new MacOutputStream(dOut, hMac);
-//        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
-// END android-removed
-// BEGIN android-added
-        HMac                    hMac = new HMac(OpenSSLMessageDigest.getInstance("SHA-1"));
+        // BEGIN android-changed
+        HMac                    hMac = new HMac(new OpenSSLDigest.SHA1());
         MacOutputStream         mOut = new MacOutputStream(dOut, hMac);
-        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(OpenSSLMessageDigest.getInstance("SHA-1"));
-// END android-added
+        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new OpenSSLDigest.SHA1());
+        // END android-changed
         byte[]                  passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
 
         pbeGen.init(passKey, salt, iterationCount);
@@ -961,7 +927,6 @@
                 return;
             }
     
-            Cipher              cipher;
             DataInputStream     dIn = new DataInputStream(stream);
             int                 version = dIn.readInt();
     
@@ -989,39 +954,36 @@
                 throw new IOException("Key store corrupted.");
             }
     
+            String cipherAlg;
             if (version == 0)
             {
-                cipher = this.makePBECipher("Old" + STORE_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+                cipherAlg = "Old" + STORE_CIPHER;
             }
             else
             {
-                cipher = this.makePBECipher(STORE_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+                cipherAlg = STORE_CIPHER;
             }
-    
-            CipherInputStream  cIn = new CipherInputStream(dIn, cipher);
 
-// BEGIN android-removed
-//            DigestInputStream  dgIn = new DigestInputStream(cIn, new SHA1Digest());
-// END android-removed
-// BEGIN android-added
-            DigestInputStream  dgIn = new DigestInputStream(cIn, OpenSSLMessageDigest.getInstance("SHA-1"));
-// END android-added
-            
+            Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+            CipherInputStream cIn = new CipherInputStream(dIn, cipher);
+
+            // BEGIN android-changed
+            Digest dig = new OpenSSLDigest.SHA1();
+            // END android-changed
+            DigestInputStream  dgIn = new DigestInputStream(cIn, dig);
+    
             this.loadStore(dgIn);
-    
-            Digest  dig = dgIn.getDigest();
-            int     digSize = dig.getDigestSize();
-            byte[]  hash = new byte[digSize];
-            byte[]  oldHash = new byte[digSize];
-    
+
+            // Finalise our digest calculation
+            byte[] hash = new byte[dig.getDigestSize()];
             dig.doFinal(hash, 0);
-    
-            for (int i = 0; i != digSize; i++)
-            {
-                oldHash[i] = (byte)cIn.read();
-            }
-    
-            if (!this.isSameAs(hash, oldHash))
+
+            // TODO Should this actually be reading the remainder of the stream?
+            // Read the original digest from the stream
+            byte[] oldHash = new byte[dig.getDigestSize()];
+            Streams.readFully(cIn, oldHash);
+
+            if (!Arrays.constantTimeAreEqual(hash, oldHash))
             {
                 table.clear();
                 throw new IOException("KeyStore integrity check failed.");
@@ -1047,12 +1009,9 @@
             cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
     
             CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
-// BEGIN android-removed            
-//            DigestOutputStream  dgOut = new DigestOutputStream(cOut, new SHA1Digest());
-// END android-removed    
-// BEGIN android-added            
-            DigestOutputStream  dgOut = new DigestOutputStream(cOut, OpenSSLMessageDigest.getInstance("SHA-1"));
-//END android-added   
+            // BEGIN android-changed
+            DigestOutputStream  dgOut = new DigestOutputStream(cOut, new OpenSSLDigest.SHA1());
+            // END android-changed
             this.saveStore(dgOut);
     
             Digest  dig = dgOut.getDigest();
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKMessageDigest.java b/src/main/java/org/bouncycastle/jce/provider/JDKMessageDigest.java
index bc8f7dd..0accdc4 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKMessageDigest.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKMessageDigest.java
@@ -57,36 +57,38 @@
         {
             super(new SHA1Digest());
         }
-
+    
         public Object clone()
             throws CloneNotSupportedException
         {
             SHA1 d = (SHA1)super.clone();
             d.digest = new SHA1Digest((SHA1Digest)digest);
-
+    
             return d;
         }
     }
-
-    static public class SHA224
-        extends JDKMessageDigest
-        implements Cloneable
-    {
-        public SHA224()
-        {
-            super(new SHA224Digest());
-        }
-
-        public Object clone()
-            throws CloneNotSupportedException
-        {
-            SHA224 d = (SHA224)super.clone();
-            d.digest = new SHA224Digest((SHA224Digest)digest);
-
-            return d;
-        }
-    }
-
+    
+    // BEGIN android-removed
+    // static public class SHA224
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public SHA224()
+    //     {
+    //         super(new SHA224Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         SHA224 d = (SHA224)super.clone();
+    //         d.digest = new SHA224Digest((SHA224Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    // END android-removed
+    
     static public class SHA256
         extends JDKMessageDigest
         implements Cloneable
@@ -95,13 +97,13 @@
         {
             super(new SHA256Digest());
         }
-
+    
         public Object clone()
             throws CloneNotSupportedException
         {
             SHA256 d = (SHA256)super.clone();
             d.digest = new SHA256Digest((SHA256Digest)digest);
-
+    
             return d;
         }
     }
@@ -144,45 +146,45 @@
         }
     }
 
-// BEGIN android-removed
-//    static public class MD2
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public MD2()
-//        {
-//            super(new MD2Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            MD2 d = (MD2)super.clone();
-//            d.digest = new MD2Digest((MD2Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//
-//    static public class MD4
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public MD4()
-//        {
-//            super(new MD4Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            MD4 d = (MD4)super.clone();
-//            d.digest = new MD4Digest((MD4Digest)digest);
-//
-//            return d;
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // static public class MD2
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public MD2()
+    //     {
+    //         super(new MD2Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         MD2 d = (MD2)super.clone();
+    //         d.digest = new MD2Digest((MD2Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //
+    // static public class MD4
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public MD4()
+    //     {
+    //         super(new MD4Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         MD4 d = (MD4)super.clone();
+    //         d.digest = new MD4Digest((MD4Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    // END android-removed
 
     static public class MD5
         extends JDKMessageDigest
@@ -192,149 +194,149 @@
         {
             super(new MD5Digest());
         }
-
+   
         public Object clone()
             throws CloneNotSupportedException
         {
             MD5 d = (MD5)super.clone();
             d.digest = new MD5Digest((MD5Digest)digest);
-
+   
             return d;
         }
     }
 
-// BEGIN android-removed
-//    static public class RIPEMD128
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public RIPEMD128()
-//        {
-//            super(new RIPEMD128Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            RIPEMD128 d = (RIPEMD128)super.clone();
-//            d.digest = new RIPEMD128Digest((RIPEMD128Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//
-//    static public class RIPEMD160
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public RIPEMD160()
-//        {
-//            super(new RIPEMD160Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            RIPEMD160 d = (RIPEMD160)super.clone();
-//            d.digest = new RIPEMD160Digest((RIPEMD160Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//    
-//    static public class RIPEMD256
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public RIPEMD256()
-//        {
-//            super(new RIPEMD256Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            RIPEMD256 d = (RIPEMD256)super.clone();
-//            d.digest = new RIPEMD256Digest((RIPEMD256Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//    
-//    static public class RIPEMD320
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public RIPEMD320()
-//        {
-//            super(new RIPEMD320Digest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            RIPEMD320 d = (RIPEMD320)super.clone();
-//            d.digest = new RIPEMD320Digest((RIPEMD320Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//    
-//    static public class Tiger
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public Tiger()
-//        {
-//            super(new TigerDigest());
-//        }
-//
-//        public Object clone()
-//            throws CloneNotSupportedException
-//        {
-//            Tiger d = (Tiger)super.clone();
-//            d.digest = new TigerDigest((TigerDigest)digest);
-//
-//            return d;
-//        }
-//    }
-//    
-//    static public class GOST3411
-//        extends JDKMessageDigest
-//        implements Cloneable
-//    {
-//        public GOST3411()
-//        {
-//            super(new GOST3411Digest());
-//        }
-//    
-//        public Object clone()
-//        throws CloneNotSupportedException
-//        {
-//            GOST3411 d = (GOST3411)super.clone();
-//            d.digest = new GOST3411Digest((GOST3411Digest)digest);
-//
-//            return d;
-//        }
-//    }
-//    
-//    static public class Whirlpool
-//       extends JDKMessageDigest
-//       implements Cloneable
-//    {
-//        public Whirlpool()
-//        {
-//            super(new WhirlpoolDigest());
-//        }
-//        
-//        public Object clone()
-//        throws CloneNotSupportedException
-//        {
-//            Whirlpool d = (Whirlpool)super.clone();
-//            d.digest = new WhirlpoolDigest((WhirlpoolDigest)digest);
-//            
-//            return d;
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // static public class RIPEMD128
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public RIPEMD128()
+    //     {
+    //         super(new RIPEMD128Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         RIPEMD128 d = (RIPEMD128)super.clone();
+    //         d.digest = new RIPEMD128Digest((RIPEMD128Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //
+    // static public class RIPEMD160
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public RIPEMD160()
+    //     {
+    //         super(new RIPEMD160Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         RIPEMD160 d = (RIPEMD160)super.clone();
+    //         d.digest = new RIPEMD160Digest((RIPEMD160Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //   
+    // static public class RIPEMD256
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public RIPEMD256()
+    //     {
+    //         super(new RIPEMD256Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         RIPEMD256 d = (RIPEMD256)super.clone();
+    //         d.digest = new RIPEMD256Digest((RIPEMD256Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //   
+    // static public class RIPEMD320
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public RIPEMD320()
+    //     {
+    //         super(new RIPEMD320Digest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         RIPEMD320 d = (RIPEMD320)super.clone();
+    //         d.digest = new RIPEMD320Digest((RIPEMD320Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //   
+    // static public class Tiger
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public Tiger()
+    //     {
+    //         super(new TigerDigest());
+    //     }
+    //
+    //     public Object clone()
+    //         throws CloneNotSupportedException
+    //     {
+    //         Tiger d = (Tiger)super.clone();
+    //         d.digest = new TigerDigest((TigerDigest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //   
+    // static public class GOST3411
+    //     extends JDKMessageDigest
+    //     implements Cloneable
+    // {
+    //     public GOST3411()
+    //     {
+    //         super(new GOST3411Digest());
+    //     }
+    //   
+    //     public Object clone()
+    //     throws CloneNotSupportedException
+    //     {
+    //         GOST3411 d = (GOST3411)super.clone();
+    //         d.digest = new GOST3411Digest((GOST3411Digest)digest);
+    //
+    //         return d;
+    //     }
+    // }
+    //   
+    // static public class Whirlpool
+    //    extends JDKMessageDigest
+    //    implements Cloneable
+    // {
+    //     public Whirlpool()
+    //     {
+    //         super(new WhirlpoolDigest());
+    //     }
+    //       
+    //     public Object clone()
+    //     throws CloneNotSupportedException
+    //     {
+    //         Whirlpool d = (Whirlpool)super.clone();
+    //         d.digest = new WhirlpoolDigest((WhirlpoolDigest)digest);
+    //           
+    //         return d;
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java b/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
index 3cf05b1..080fdf3 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
@@ -12,6 +12,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PrivateKey;
+import java.security.Provider;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
@@ -33,17 +34,18 @@
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.BERConstructedOctetString;
 import org.bouncycastle.asn1.BEROutputStream;
 import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DEREncodable;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.pkcs.AuthenticatedSafe;
@@ -65,28 +67,22 @@
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.jce.interfaces.BCKeyStore;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
 public class JDKPKCS12KeyStore
     extends KeyStoreSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers, BCKeyStore
 {
-    private static final int    SALT_SIZE = 20;
-    private static final int    MIN_ITERATIONS = 100;
-    
-    //
-    // SHA-1 and 3-key-triple DES.
-    //
-    private static final String KEY_ALGORITHM = "1.2.840.113549.1.12.1.3";
+    private static final int                SALT_SIZE = 20;
+    private static final int                MIN_ITERATIONS = 1024;
 
-    //
-    // SHA-1 and 40 bit RC2.
-    //
-    private static final String CERT_ALGORITHM = "1.2.840.113549.1.12.1.6";
+    private static final Provider           bcProvider = new BouncyCastleProvider();
 
-    private Hashtable                       keys = new Hashtable();
+    private IgnoresCaseHashtable            keys = new IgnoresCaseHashtable();
     private Hashtable                       localIds = new Hashtable();
-    private Hashtable                       certs = new Hashtable();
+    private IgnoresCaseHashtable            certs = new IgnoresCaseHashtable();
     private Hashtable                       chainCerts = new Hashtable();
     private Hashtable                       keyCerts = new Hashtable();
 
@@ -108,7 +104,10 @@
 
     protected SecureRandom      random = new SecureRandom();
 
-    private CertificateFactory  certFact = null;
+    // use of final causes problems with JDK 1.2 compiler
+    private CertificateFactory  certFact;
+    private DERObjectIdentifier keyAlgorithm;
+    private DERObjectIdentifier certAlgorithm;
 
     private class CertId
     {
@@ -128,20 +127,17 @@
 
         public int hashCode()
         {
-            int hash = id[0] & 0xff;
-
-            for (int i = 1; i != id.length - 4; i++)
-            {
-                hash ^= ((id[i] & 0xff) << 24) | ((id[i + 1] & 0xff) << 16)
-                          | ((id[i + 2] & 0xff) << 8) | (id[i + 3] & 0xff);
-            }
-
-            return hash;
+            return Arrays.hashCode(id);
         }
 
         public boolean equals(
             Object  o)
         {
+            if (o == this)
+            {
+                return true;
+            }
+
             if (!(o instanceof CertId))
             {
                 return false;
@@ -149,26 +145,18 @@
 
             CertId  cId = (CertId)o;
 
-            if (cId.id.length != id.length)
-            {
-                return false;
-            }
-
-            for (int i = 0; i != id.length; i++)
-            {
-                if (cId.id[i] != id[i])
-                {
-                    return false;
-                }
-            }
-
-            return true;
+            return Arrays.areEqual(id, cId.id);
         }
     }
 
     public JDKPKCS12KeyStore(
-        String provider)
+        Provider provider,
+        DERObjectIdentifier keyAlgorithm,
+        DERObjectIdentifier certAlgorithm)
     {
+        this.keyAlgorithm = keyAlgorithm;
+        this.certAlgorithm = certAlgorithm;
+
         try
         {
             if (provider != null)
@@ -192,7 +180,7 @@
         try
         {
             SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
-                (ASN1Sequence)new ASN1InputStream(pubKey.getEncoded()).readObject());
+                (ASN1Sequence) ASN1Object.fromByteArray(pubKey.getEncoded()));
 
             return new SubjectKeyIdentifier(info);
         }
@@ -238,7 +226,7 @@
     }
 
     /**
-     * this is quite complete - we should follow up on the chain, a bit
+     * this is not quite complete - we should follow up on the chain, a bit
      * tricky if a certificate appears in more than one chain...
      */
     public void engineDeleteEntry(
@@ -478,9 +466,9 @@
         Certificate cert) 
         throws KeyStoreException
     {
-        if (certs.get(alias) != null)
+        if (keys.get(alias) != null)
         {
-            throw new KeyStoreException("There is already a certificate with the name " + alias + ".");
+            throw new KeyStoreException("There is a key entry with the name " + alias + ".");
         }
 
         certs.put(alias, cert);
@@ -508,9 +496,9 @@
             throw new KeyStoreException("no certificate chain for private key");
         }
 
-        if (keys.get(alias) != null && !key.equals(keys.get(alias)))
+        if (keys.get(alias) != null)
         {
-            throw new KeyStoreException("There is already a key with the name " + alias + ".");
+            engineDeleteEntry(alias);
         }
 
         keys.put(alias, key);
@@ -556,12 +544,12 @@
         PKCS12PBEParams     pbeParams = new PKCS12PBEParams((ASN1Sequence)algId.getParameters());
 
         PBEKeySpec          pbeSpec = new PBEKeySpec(password);
-        PrivateKey          out = null;
+        PrivateKey          out;
 
         try
         {
             SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
-                                                algorithm, "BC");
+                                                algorithm, bcProvider);
             PBEParameterSpec    defParams = new PBEParameterSpec(
                                                 pbeParams.getIV(),
                                                 pbeParams.getIterations().intValue());
@@ -570,7 +558,7 @@
             
             ((JCEPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero);
 
-            Cipher cipher = Cipher.getInstance(algorithm, "BC");
+            Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
 
             cipher.init(Cipher.UNWRAP_MODE, k, defParams);
 
@@ -598,12 +586,12 @@
         try
         {
             SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
-                                                algorithm, "BC");
+                                                algorithm, bcProvider);
             PBEParameterSpec    defParams = new PBEParameterSpec(
                                                 pbeParams.getIV(),
                                                 pbeParams.getIterations().intValue());
 
-            Cipher cipher = Cipher.getInstance(algorithm, "BC");
+            Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
 
             cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), defParams);
 
@@ -617,76 +605,37 @@
         return out;
     }
 
-    protected ASN1Sequence decryptData(
+    protected byte[] cryptData(
+        boolean               forEncryption,
         AlgorithmIdentifier   algId,
-        byte[]                data,
         char[]                password,
-        boolean               wrongPKCS12Zero)
+        boolean               wrongPKCS12Zero,
+        byte[]                data)
         throws IOException
     {
-        String              algorithm = algId.getObjectId().getId();
-        PKCS12PBEParams     pbeParams = new PKCS12PBEParams((ASN1Sequence)algId.getParameters());
-
-        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
-        byte[]              out = null;
+        String          algorithm = algId.getObjectId().getId();
+        PKCS12PBEParams pbeParams = new PKCS12PBEParams((ASN1Sequence)algId.getParameters());
+        PBEKeySpec      pbeSpec = new PBEKeySpec(password);
 
         try
         {
-            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
-                                                algorithm, "BC");
-            PBEParameterSpec    defParams = new PBEParameterSpec(
-                                                pbeParams.getIV(),
-                                                pbeParams.getIterations().intValue());
-            SecretKey           k = keyFact.generateSecret(pbeSpec);
-            
-            ((JCEPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero);
+            SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider);
+            PBEParameterSpec defParams = new PBEParameterSpec(
+                pbeParams.getIV(),
+                pbeParams.getIterations().intValue());
+            JCEPBEKey        key = (JCEPBEKey) keyFact.generateSecret(pbeSpec);
 
-            Cipher cipher = Cipher.getInstance(algorithm, "BC");
+            key.setTryWrongPKCS12Zero(wrongPKCS12Zero);
 
-            cipher.init(Cipher.DECRYPT_MODE, k, defParams);
-
-            out = cipher.doFinal(data);
+            Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
+            int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+            cipher.init(mode, key, defParams);
+            return cipher.doFinal(data);
         }
         catch (Exception e)
         {
             throw new IOException("exception decrypting data - " + e.toString());
         }
-
-        ASN1InputStream  aIn = new ASN1InputStream(out);
-
-        return (ASN1Sequence)aIn.readObject();
-    }
-
-    protected byte[] encryptData(
-        String                  algorithm,
-        byte[]                  data,
-        PKCS12PBEParams         pbeParams,
-        char[]                  password)
-        throws IOException
-    {
-        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
-        byte[]              out;
-
-        try
-        {
-            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
-                                                algorithm, "BC");
-            PBEParameterSpec    defParams = new PBEParameterSpec(
-                                                pbeParams.getIV(),
-                                                pbeParams.getIterations().intValue());
-
-            Cipher cipher = Cipher.getInstance(algorithm, "BC");
-
-            cipher.init(Cipher.ENCRYPT_MODE, keyFact.generateSecret(pbeSpec), defParams);
-
-            out = cipher.doFinal(data);
-        }
-        catch (Exception e)
-        {
-            throw new IOException("exception encrypting data - " + e.toString());
-        }
-
-        return out;
     }
 
     public void engineLoad(
@@ -727,79 +676,34 @@
 
         if (bag.getMacData() != null)           // check the mac code
         {
-            ByteArrayOutputStream       bOut = new ByteArrayOutputStream();
-            BEROutputStream             berOut = new BEROutputStream(bOut);
             MacData                     mData = bag.getMacData();
             DigestInfo                  dInfo = mData.getMac();
             AlgorithmIdentifier         algId = dInfo.getAlgorithmId();
             byte[]                      salt = mData.getSalt();
             int                         itCount = mData.getIterationCount().intValue();
-        
-            berOut.writeObject(info);
 
             byte[]  data = ((ASN1OctetString)info.getContent()).getOctets();
 
             try
             {
-                Mac                 mac = Mac.getInstance(algId.getObjectId().getId(), "BC");
-                SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(algId.getObjectId().getId(), "BC");
-                PBEParameterSpec    defParams = new PBEParameterSpec(salt, itCount);
-                PBEKeySpec          pbeSpec = new PBEKeySpec(password);
+                byte[] res = calculatePbeMac(algId.getObjectId(), salt, itCount, password, false, data);
+                byte[] dig = dInfo.getDigest();
 
-                mac.init(keyFact.generateSecret(pbeSpec), defParams);
-
-                mac.update(data);
-
-                byte[]  res = mac.doFinal();
-                byte[]  dig = dInfo.getDigest();
-
-                if (res.length != dInfo.getDigest().length)
+                if (!Arrays.constantTimeAreEqual(res, dig))
                 {
-                    throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
-                }
-
-                boolean okay = true;
-                
-                for (int i = 0; i != res.length; i++)
-                {
-                    if (res[i] != dig[i])
+                    if (password.length > 0)
                     {
-                        if (password.length != 0)  // may be dodgey zero password
-                        {
-                            throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
-                        }
-                        else
-                        {
-                            okay = false;
-                            break;
-                        }
+                        throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
                     }
-                }
-                
-                //
-                // may be incorrect zero length password
-                //
-                if (!okay)
-                {
-                    SecretKey k = keyFact.generateSecret(pbeSpec);
-                    
-                    ((JCEPBEKey)k).setTryWrongPKCS12Zero(true);
-                    
-                    mac.init(k, defParams);
-    
-                    mac.update(data);
-    
-                    res = mac.doFinal();
-                    dig = dInfo.getDigest();
-                    
-                    for (int i = 0; i != res.length; i++)
+
+                    // Try with incorrect zero length password
+                    res = calculatePbeMac(algId.getObjectId(), salt, itCount, password, true, data);
+
+                    if (!Arrays.constantTimeAreEqual(res, dig))
                     {
-                        if (res[i] != dig[i])
-                        {
-                           throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
-                        }
+                        throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
                     }
-                    
+
                     wrongPKCS12Zero = true;
                 }
             }
@@ -813,7 +717,7 @@
             }
         }
 
-        keys = new Hashtable();
+        keys = new IgnoresCaseHashtable();
         localIds = new Hashtable();
 
         if (info.getContentType().equals(data))
@@ -858,8 +762,21 @@
                                     if (attrSet.size() > 0)
                                     {
                                         attr = (DERObject)attrSet.getObjectAt(0);
-    
-                                        bagAttr.setBagAttribute(aOid, attr);
+
+                                        DEREncodable existing = bagAttr.getBagAttribute(aOid);
+                                        if (existing != null)
+                                        {
+                                            // OK, but the value has to be the same
+                                            if (!existing.getDERObject().equals(attr))
+                                            {
+                                                throw new IOException(
+                                                    "attempt to add existing attribute with different value");
+                                            }
+                                        }
+                                        else
+                                        {
+                                            bagAttr.setBagAttribute(aOid, attr);
+                                        }
                                     }
     
                                     if (aOid.equals(pkcs_9_at_friendlyName))
@@ -907,7 +824,9 @@
                 else if (c[i].getContentType().equals(encryptedData))
                 {
                     EncryptedData d = new EncryptedData((ASN1Sequence)c[i].getContent());
-                    ASN1Sequence seq = decryptData(d.getEncryptionAlgorithm(), d.getContent().getOctets(), password, wrongPKCS12Zero);
+                    byte[] octets = cryptData(false, d.getEncryptionAlgorithm(),
+                        password, wrongPKCS12Zero, d.getContent().getOctets());
+                    ASN1Sequence seq = (ASN1Sequence) ASN1Object.fromByteArray(octets);
 
                     for (int j = 0; j != seq.size(); j++)
                     {
@@ -941,7 +860,20 @@
                                 {
                                     attr = (DERObject)attrSet.getObjectAt(0);
 
-                                    bagAttr.setBagAttribute(aOid, attr);
+                                    DEREncodable existing = bagAttr.getBagAttribute(aOid);
+                                    if (existing != null)
+                                    {
+                                        // OK, but the value has to be the same
+                                        if (!existing.getDERObject().equals(attr))
+                                        {
+                                            throw new IOException(
+                                                "attempt to add existing attribute with different value");
+                                        }
+                                    }
+                                    else
+                                    {
+                                        bagAttr.setBagAttribute(aOid, attr);
+                                    }
                                 }
 
                                 if (aOid.equals(pkcs_9_at_friendlyName))
@@ -990,7 +922,20 @@
                                 {
                                     attr = (DERObject)attrSet.getObjectAt(0);
 
-                                    bagAttr.setBagAttribute(aOid, attr);
+                                    DEREncodable existing = bagAttr.getBagAttribute(aOid);
+                                    if (existing != null)
+                                    {
+                                        // OK, but the value has to be the same
+                                        if (!existing.getDERObject().equals(attr))
+                                        {
+                                            throw new IOException(
+                                                "attempt to add existing attribute with different value");
+                                        }
+                                    }
+                                    else
+                                    {
+                                        bagAttr.setBagAttribute(aOid, attr);
+                                    }
                                 }
 
                                 if (aOid.equals(pkcs_9_at_friendlyName))
@@ -1030,7 +975,7 @@
             }
         }
 
-        certs = new Hashtable();
+        certs = new IgnoresCaseHashtable();
         chainCerts = new Hashtable();
         keyCerts = new Hashtable();
 
@@ -1038,7 +983,13 @@
         {
             SafeBag     b = (SafeBag)chain.elementAt(i);
             CertBag     cb = new CertBag((ASN1Sequence)b.getBagValue());
-            Certificate cert = null;
+
+            if (!cb.getCertId().equals(x509Certificate))
+            {
+                throw new RuntimeException("Unsupported certificate type: " + cb.getCertId());
+            }
+
+            Certificate cert;
 
             try
             {
@@ -1051,12 +1002,11 @@
                 throw new RuntimeException(e.toString());
             }
 
-
             //
             // set the attributes
             //
-            ASN1OctetString              localId = null;
-            String                      alias = null;
+            ASN1OctetString localId = null;
+            String          alias = null;
 
             if (b.getBagAttributes() != null)
             {
@@ -1066,11 +1016,26 @@
                     ASN1Sequence  sq = (ASN1Sequence)e.nextElement();
                     DERObjectIdentifier     oid = (DERObjectIdentifier)sq.getObjectAt(0);
                     DERObject               attr = (DERObject)((ASN1Set)sq.getObjectAt(1)).getObjectAt(0);
+                    PKCS12BagAttributeCarrier   bagAttr = null;
 
                     if (cert instanceof PKCS12BagAttributeCarrier)
                     {
-                        PKCS12BagAttributeCarrier   bagAttr = (PKCS12BagAttributeCarrier)cert;
-                        bagAttr.setBagAttribute(oid, attr);
+                        bagAttr = (PKCS12BagAttributeCarrier)cert;
+
+                        DEREncodable existing = bagAttr.getBagAttribute(oid);
+                        if (existing != null)
+                        {
+                            // OK, but the value has to be the same
+                            if (!existing.getDERObject().equals(attr))
+                            {
+                                throw new IOException(
+                                    "attempt to add existing attribute with different value");
+                            }
+                        }
+                        else
+                        {
+                            bagAttr.setBagAttribute(oid, attr);
+                        }
                     }
 
                     if (oid.equals(pkcs_9_at_friendlyName))
@@ -1123,9 +1088,6 @@
             throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
         }
 
-        ContentInfo[]   c = new ContentInfo[2];
-
-
         //
         // handle the key
         //
@@ -1143,8 +1105,8 @@
             String                  name = (String)ks.nextElement();
             PrivateKey              privKey = (PrivateKey)keys.get(name);
             PKCS12PBEParams         kParams = new PKCS12PBEParams(kSalt, MIN_ITERATIONS);
-            byte[]                  kBytes = wrapKey(KEY_ALGORITHM, privKey, kParams, password);
-            AlgorithmIdentifier     kAlgId = new AlgorithmIdentifier(new DERObjectIdentifier(KEY_ALGORITHM), kParams.getDERObject());
+            byte[]                  kBytes = wrapKey(keyAlgorithm.getId(), privKey, kParams, password);
+            AlgorithmIdentifier     kAlgId = new AlgorithmIdentifier(keyAlgorithm, kParams.getDERObject());
             org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo kInfo = new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo(kAlgId, kBytes);
             boolean                 attrSet = false;
             ASN1EncodableVector     kName = new ASN1EncodableVector();
@@ -1212,15 +1174,11 @@
             keyS.add(kBag);
         }
 
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
-        dOut.writeObject(new DERSequence(keyS));
-
-        BERConstructedOctetString          keyString = new BERConstructedOctetString(bOut.toByteArray());
+        byte[]                    keySEncoded = new DERSequence(keyS).getDEREncoded();
+        BERConstructedOctetString keyString = new BERConstructedOctetString(keySEncoded);
 
         //
-        // certficate processing
+        // certificate processing
         //
         byte[]                  cSalt = new byte[SALT_SIZE];
 
@@ -1228,7 +1186,7 @@
 
         ASN1EncodableVector  certSeq = new ASN1EncodableVector();
         PKCS12PBEParams         cParams = new PKCS12PBEParams(cSalt, MIN_ITERATIONS);
-        AlgorithmIdentifier     cAlgId = new AlgorithmIdentifier(new DERObjectIdentifier(CERT_ALGORITHM), cParams.getDERObject());
+        AlgorithmIdentifier     cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.getDERObject());
         Hashtable               doneCerts = new Hashtable();
 
         Enumeration cs = keys.keys();
@@ -1240,7 +1198,7 @@
                 Certificate         cert = engineGetCertificate(name);
                 boolean             cAttrSet = false;
                 CertBag             cBag = new CertBag(
-                                        x509certType,
+                                        x509Certificate,
                                         new DEROctetString(cert.getEncoded()));
                 ASN1EncodableVector fName = new ASN1EncodableVector();
 
@@ -1322,7 +1280,7 @@
                 }
 
                 CertBag             cBag = new CertBag(
-                                        x509certType,
+                                        x509Certificate,
                                         new DEROctetString(cert.getEncoded()));
                 ASN1EncodableVector fName = new ASN1EncodableVector();
 
@@ -1343,6 +1301,16 @@
                     while (e.hasMoreElements())
                     {
                         DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+
+                        // a certificate not immediately linked to a key doesn't require
+                        // a localKeyID and will confuse some PKCS12 implementations.
+                        //
+                        // If we find one, we'll prune it out.
+                        if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId))
+                        {
+                            continue;
+                        }
+
                         ASN1EncodableVector fSeq = new ASN1EncodableVector();
 
                         fSeq.add(oid);
@@ -1389,7 +1357,7 @@
                 }
 
                 CertBag             cBag = new CertBag(
-                                        x509certType,
+                                        x509Certificate,
                                         new DEROctetString(cert.getEncoded()));
                 ASN1EncodableVector fName = new ASN1EncodableVector();
 
@@ -1401,6 +1369,16 @@
                     while (e.hasMoreElements())
                     {
                         DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+
+                        // a certificate not immediately linked to a key doesn't require
+                        // a localKeyID and will confuse some PKCS12 implementations.
+                        //
+                        // If we find one, we'll prune it out.
+                        if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId))
+                        {
+                            continue;
+                        }
+
                         ASN1EncodableVector fSeq = new ASN1EncodableVector();
 
                         fSeq.add(oid);
@@ -1419,25 +1397,19 @@
             }
         }
 
-        bOut.reset();
+        byte[]          certSeqEncoded = new DERSequence(certSeq).getDEREncoded();
+        byte[]          certBytes = cryptData(true, cAlgId, password, false, certSeqEncoded);
+        EncryptedData   cInfo = new EncryptedData(data, cAlgId, new BERConstructedOctetString(certBytes));
 
-        dOut = new DEROutputStream(bOut);
+        ContentInfo[] info = new ContentInfo[]
+        {
+            new ContentInfo(data, keyString),
+            new ContentInfo(encryptedData, cInfo.getDERObject())
+        };
 
-        dOut.writeObject(new DERSequence(certSeq));
+        AuthenticatedSafe   auth = new AuthenticatedSafe(info);
 
-        dOut.close();
-
-        byte[]                  certBytes = encryptData(CERT_ALGORITHM, bOut.toByteArray(), cParams, password);
-        EncryptedData           cInfo = new EncryptedData(data, cAlgId, new BERConstructedOctetString(certBytes));
-
-        c[0] = new ContentInfo(data, keyString);
-
-        c[1] = new ContentInfo(encryptedData, cInfo.getDERObject());
-
-        AuthenticatedSafe   auth = new AuthenticatedSafe(c);
-
-        bOut.reset();
-
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
         BEROutputStream         berOut = new BEROutputStream(bOut);
 
         berOut.writeObject(auth);
@@ -1456,23 +1428,14 @@
     
         byte[]  data = ((ASN1OctetString)mainInfo.getContent()).getOctets();
 
-        MacData                 mData = null;
+        MacData                 mData;
 
         try
         {
-            Mac                 mac = Mac.getInstance(id_SHA1.getId(), "BC");
-            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(id_SHA1.getId(), "BC");
-            PBEParameterSpec    defParams = new PBEParameterSpec(mSalt, itCount);
-            PBEKeySpec          pbeSpec = new PBEKeySpec(password);
-
-            mac.init(keyFact.generateSecret(pbeSpec), defParams);
-
-            mac.update(data);
-
-            byte[]      res = mac.doFinal();
+            byte[] res = calculatePbeMac(id_SHA1, mSalt, itCount, password, false, data);
 
             // BEGIN android-changed
-            AlgorithmIdentifier     algId = new AlgorithmIdentifier(id_SHA1, DERNull.THE_ONE);
+            AlgorithmIdentifier     algId = new AlgorithmIdentifier(id_SHA1, DERNull.INSTANCE);
             // END android-changed
             DigestInfo              dInfo = new DigestInfo(algId, res);
 
@@ -1493,21 +1456,113 @@
         berOut.writeObject(pfx);
     }
 
+    private static byte[] calculatePbeMac(
+        DERObjectIdentifier oid,
+        byte[]              salt,
+        int                 itCount,
+        char[]              password,
+        boolean             wrongPkcs12Zero,
+        byte[]              data)
+        throws Exception
+    {
+        SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(oid.getId(), bcProvider);
+        PBEParameterSpec    defParams = new PBEParameterSpec(salt, itCount);
+        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
+        JCEPBEKey           key = (JCEPBEKey) keyFact.generateSecret(pbeSpec);
+        key.setTryWrongPKCS12Zero(wrongPkcs12Zero);
+
+        Mac mac = Mac.getInstance(oid.getId(), bcProvider);
+        mac.init(key, defParams);
+        mac.update(data);
+        return mac.doFinal();
+    }
+    
     public static class BCPKCS12KeyStore
         extends JDKPKCS12KeyStore
     {
         public BCPKCS12KeyStore()
         {
-            super("BC");
+            super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbewithSHAAnd40BitRC2_CBC);
         }
     }
 
-    public static class DefPKCS12KeyStore
-        extends JDKPKCS12KeyStore
+    // BEGIN android-removed
+    // public static class BCPKCS12KeyStore3DES
+    //     extends JDKPKCS12KeyStore
+    // {
+    //     public BCPKCS12KeyStore3DES()
+    //     {
+    //         super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+    //     }
+    // }
+    //
+    // public static class DefPKCS12KeyStore
+    //     extends JDKPKCS12KeyStore
+    // {
+    //     public DefPKCS12KeyStore()
+    //     {
+    //         super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbewithSHAAnd40BitRC2_CBC);
+    //     }
+    // }
+    //
+    // public static class DefPKCS12KeyStore3DES
+    //     extends JDKPKCS12KeyStore
+    // {
+    //     public DefPKCS12KeyStore3DES()
+    //     {
+    //         super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+    //     }
+    // }
+    // END android-removed
+
+    private static class IgnoresCaseHashtable
     {
-        public DefPKCS12KeyStore()
+        private Hashtable orig = new Hashtable();
+        private Hashtable keys = new Hashtable();
+
+        public void put(String key, Object value)
         {
-            super(null);
+            String lower = Strings.toLowerCase(key);
+            String k = (String)keys.get(lower);
+            if (k != null)
+            {
+                orig.remove(k);
+            }
+
+            keys.put(lower, key);
+            orig.put(key, value);
+        }
+
+        public Enumeration keys()
+        {
+            return orig.keys();
+        }
+
+        public Object remove(String alias)
+        {
+            String k = (String)keys.remove(Strings.toLowerCase(alias));
+            if (k == null)
+            {
+                return null;
+            }
+
+            return orig.remove(k);
+        }
+
+        public Object get(String alias)
+        {
+            String k = (String)keys.get(Strings.toLowerCase(alias));
+            if (k == null)
+            {
+                return null;
+            }
+            
+            return orig.get(k);
+        }
+
+        public Enumeration elements()
+        {
+            return orig.elements();
         }
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKPSSSigner.java b/src/main/java/org/bouncycastle/jce/provider/JDKPSSSigner.java
deleted file mode 100644
index 33df9fd..0000000
--- a/src/main/java/org/bouncycastle/jce/provider/JDKPSSSigner.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package org.bouncycastle.jce.provider;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.MGF1ParameterSpec;
-import java.security.spec.PSSParameterSpec;
-
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.crypto.AsymmetricBlockCipher;
-import org.bouncycastle.crypto.CryptoException;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.engines.RSAEngine;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.signers.PSSSigner;
-
-public class JDKPSSSigner
-    extends Signature
-{
-    private AlgorithmParameters    engineParams;
-    private PSSParameterSpec       paramSpec;
-    private PSSParameterSpec       originalSpec;
-    private AsymmetricBlockCipher  signer;
-    private Digest digest;
-    private int saltLength;
-    private byte trailer;
-
-    private PSSSigner pss;
-
-    private byte getTrailer(
-        int trailerField)
-    {
-        if (trailerField == 1)
-        {
-            return PSSSigner.TRAILER_IMPLICIT;
-        }
-        
-        throw new IllegalArgumentException("unknown trailer field");
-    }
-    
-    protected JDKPSSSigner(
-        String                name,
-        AsymmetricBlockCipher signer,
-        PSSParameterSpec      paramSpec)
-    {
-        super(name);
-
-        this.signer = signer;
-        
-        if (paramSpec == null)
-        {
-            originalSpec = null;
-            paramSpec = PSSParameterSpec.DEFAULT;
-        }
-        else
-        {
-            originalSpec = paramSpec;
-            this.paramSpec = paramSpec;
-        }
-        
-        this.digest = JCEDigestUtil.getDigest(paramSpec.getDigestAlgorithm());
-        this.saltLength = paramSpec.getSaltLength();
-        this.trailer = getTrailer(paramSpec.getTrailerField());
-    }
-    
-    protected void engineInitVerify(
-        PublicKey   publicKey)
-        throws InvalidKeyException
-    {
-        if (!(publicKey instanceof RSAPublicKey))
-        {
-            throw new InvalidKeyException("Supplied key is not a RSAPublicKey instance");
-        }
-
-        pss = new PSSSigner(signer, digest, saltLength);
-        pss.init(false,
-            RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey));
-    }
-
-    protected void engineInitSign(
-        PrivateKey      privateKey,
-        SecureRandom    random)
-        throws InvalidKeyException
-    {
-        if (!(privateKey instanceof RSAPrivateKey))
-        {
-            throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
-        }
-
-        pss = new PSSSigner(signer, digest, saltLength, trailer);
-        pss.init(true, new ParametersWithRandom(RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey), random));
-    }
-
-    protected void engineInitSign(
-        PrivateKey  privateKey)
-        throws InvalidKeyException
-    {
-        if (!(privateKey instanceof RSAPrivateKey))
-        {
-            throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
-        }
-
-        pss = new PSSSigner(signer, digest, saltLength, trailer);
-        pss.init(true, RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey));
-    }
-
-    protected void engineUpdate(
-        byte    b)
-        throws SignatureException
-    {
-        pss.update(b);
-    }
-
-    protected void engineUpdate(
-        byte[]  b,
-        int     off,
-        int     len) 
-        throws SignatureException
-    {
-        pss.update(b, off, len);
-    }
-
-    protected byte[] engineSign()
-        throws SignatureException
-    {
-        try
-        {
-            return pss.generateSignature();
-        }
-        catch (CryptoException e)
-        {
-            throw new SignatureException(e.getMessage());
-        }
-    }
-
-    protected boolean engineVerify(
-        byte[]  sigBytes) 
-        throws SignatureException
-    {
-        return pss.verifySignature(sigBytes);
-    }
-
-    protected void engineSetParameter(
-        AlgorithmParameterSpec params)
-        throws InvalidParameterException
-    {
-        if (params instanceof PSSParameterSpec)
-        {
-            paramSpec = (PSSParameterSpec)params;
-            
-            if (originalSpec != null)
-            {
-                if (!JCEDigestUtil.isSameDigest(originalSpec.getDigestAlgorithm(), paramSpec.getDigestAlgorithm()))
-                {
-                    throw new InvalidParameterException("parameter must be using " + originalSpec.getDigestAlgorithm());
-                }
-            }
-            if (!paramSpec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !paramSpec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId()))
-            {
-                throw new InvalidParameterException("unknown mask generation function specified");
-            }
-            
-            if (!(paramSpec.getMGFParameters() instanceof MGF1ParameterSpec))
-            {
-                throw new InvalidParameterException("unkown MGF parameters");
-            }
-            
-            MGF1ParameterSpec   mgfParams = (MGF1ParameterSpec)paramSpec.getMGFParameters();
-            
-            if (!JCEDigestUtil.isSameDigest(mgfParams.getDigestAlgorithm(), paramSpec.getDigestAlgorithm()))
-            {
-                throw new InvalidParameterException("digest algorithm for MGF should be the same as for PSS parameters.");
-            }
-            
-            digest = JCEDigestUtil.getDigest(mgfParams.getDigestAlgorithm());
-            
-            if (digest == null)
-            {
-                throw new InvalidParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm());
-            }
-            
-            this.saltLength = paramSpec.getSaltLength();
-            this.trailer = getTrailer(paramSpec.getTrailerField());
-        }
-        else
-        {
-            throw new InvalidParameterException("Only PSSParameterSpec supported");
-        }
-    }
-
-    protected AlgorithmParameters engineGetParameters() 
-    {
-        if (engineParams == null)
-        {
-            if (paramSpec != null)
-            {
-                try
-                {
-                    engineParams = AlgorithmParameters.getInstance("PSS", "BC");
-                    engineParams.init(paramSpec);
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException(e.toString());
-                }
-            }
-        }
-
-        return engineParams;
-    }
-    
-    /**
-     * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
-     */
-    protected void engineSetParameter(
-        String  param,
-        Object  value)
-    {
-        throw new UnsupportedOperationException("engineSetParameter unsupported");
-    }
-    
-    protected Object engineGetParameter(
-        String param)
-    {
-        throw new UnsupportedOperationException("engineGetParameter unsupported");
-    }
-
-    static public class PSSwithRSA
-        extends JDKPSSSigner
-    {
-        public PSSwithRSA()
-        {
-            super("SHA1withRSAandMGF1", new RSAEngine(), null);
-        }
-    }
-    
-    static public class SHA1withRSA
-        extends JDKPSSSigner
-    {
-        public SHA1withRSA()
-        {
-            super("SHA1withRSAandMGF1", new RSAEngine(), PSSParameterSpec.DEFAULT);
-        }
-    }
-
-    static public class SHA224withRSA
-        extends JDKPSSSigner
-    {
-        public SHA224withRSA()
-        {
-            super("SHA2224withRSAandMGF1", new RSAEngine(), new PSSParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), 28, 1));
-        }
-    }
-    
-    static public class SHA256withRSA
-        extends JDKPSSSigner
-    {
-        public SHA256withRSA()
-        {
-            super("SHA256withRSAandMGF1", new RSAEngine(), new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1));
-        }
-    }
-
-    static public class SHA384withRSA
-        extends JDKPSSSigner
-    {
-        public SHA384withRSA()
-        {
-            super("SHA384withRSAandMGF1", new RSAEngine(), new PSSParameterSpec("SHA-384", "MGF1", new MGF1ParameterSpec("SHA-384"), 48, 1));
-        }
-    }
-
-    static public class SHA512withRSA
-        extends JDKPSSSigner
-    {
-        public SHA512withRSA()
-        {
-            super("SHA512withRSAandMGF1", new RSAEngine(), new PSSParameterSpec("SHA-512", "MGF1", new MGF1ParameterSpec("SHA-512"), 64, 1));
-        }
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/provider/JDKX509CertificateFactory.java b/src/main/java/org/bouncycastle/jce/provider/JDKX509CertificateFactory.java
index 31507c6..45eab83 100644
--- a/src/main/java/org/bouncycastle/jce/provider/JDKX509CertificateFactory.java
+++ b/src/main/java/org/bouncycastle/jce/provider/JDKX509CertificateFactory.java
@@ -1,30 +1,30 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.CRL;
-import java.security.cert.CRLException;
-import java.security.cert.CertPath;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactorySpi;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.SignedData;
 import org.bouncycastle.asn1.x509.CertificateList;
 import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.util.encoders.Base64;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactorySpi;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * class for dealing with X509 certificates.
@@ -36,60 +36,20 @@
 public class JDKX509CertificateFactory
     extends CertificateFactorySpi
 {
-    private static final long  MAX_MEMORY = Runtime.getRuntime().maxMemory();
-    
-    private SignedData         sData = null;
+    private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE");
+    private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL");
+
+    private ASN1Set            sData = null;
     private int                sDataObjectCount = 0;
     private InputStream        currentStream = null;
     
-    private SignedData         sCrlData = null;
+    private ASN1Set            sCrlData = null;
     private int                sCrlDataObjectCount = 0;
     private InputStream        currentCrlStream = null;
 
-    private int getLimit(InputStream in)
-        throws IOException
-    {
-        if (in instanceof ByteArrayInputStream)
-        {
-            return in.available();
-        }
-        
-        if (MAX_MEMORY > Integer.MAX_VALUE)
-        {
-            return Integer.MAX_VALUE;
-        }
-        
-        return (int)MAX_MEMORY;
-    }
-    
-    private String readLine(
-        InputStream in)
-        throws IOException
-    {
-        int             c;
-        StringBuffer    l = new StringBuffer();
-
-        while (((c = in.read()) != '\n') && (c >= 0))
-        {
-            if (c == '\r')
-            {
-                continue;
-            }
-
-            l.append((char)c);
-        }
-
-        if (c < 0)
-        {
-            return null;
-        }
-
-        return l.toString();
-    }
-
     private Certificate readDERCertificate(
         ASN1InputStream dIn)
-        throws IOException
+        throws IOException, CertificateParsingException
     {
         ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
 
@@ -99,11 +59,9 @@
             if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
             {
                 sData = new SignedData(ASN1Sequence.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true));
+                                (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates();
 
-                return new X509CertificateObject(
-                            X509CertificateStructure.getInstance(
-                                    sData.getCertificates().getObjectAt(sDataObjectCount++)));
+                return getCertificate();
             }
         }
 
@@ -111,117 +69,67 @@
                             X509CertificateStructure.getInstance(seq));
     }
 
-    /**
-     * read in a BER encoded PKCS7 certificate.
-     */
-    private Certificate readPKCS7Certificate(
-        InputStream  in)
-        throws IOException
+    private Certificate getCertificate()
+        throws CertificateParsingException
     {
-        ASN1InputStream  dIn = new ASN1InputStream(in, getLimit(in));
-        ASN1Sequence     seq = (ASN1Sequence)dIn.readObject();
-
-        if (seq.size() > 1
-                && seq.getObjectAt(0) instanceof DERObjectIdentifier)
+        if (sData != null)
         {
-            if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
+            while (sDataObjectCount < sData.size())
             {
-                sData = new SignedData(ASN1Sequence.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true));
-    
-                return new X509CertificateObject(
-                            X509CertificateStructure.getInstance(
-                                    sData.getCertificates().getObjectAt(sDataObjectCount++)));
+                Object obj = sData.getObjectAt(sDataObjectCount++);
+
+                if (obj instanceof ASN1Sequence)
+                {
+                   return new X509CertificateObject(
+                                    X509CertificateStructure.getInstance(obj));
+                }
             }
         }
 
-        return new X509CertificateObject(
-                     X509CertificateStructure.getInstance(seq));
+        return null;
     }
 
     private Certificate readPEMCertificate(
         InputStream  in)
-        throws IOException
+        throws IOException, CertificateParsingException
     {
-        String          line;
-        StringBuffer    pemBuf = new StringBuffer();
+        ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in);
 
-        while ((line = readLine(in)) != null)
+        if (seq != null)
         {
-            if (line.equals("-----BEGIN CERTIFICATE-----")
-                || line.equals("-----BEGIN X509 CERTIFICATE-----"))
-            {
-                break;
-            }
+            return new X509CertificateObject(
+                            X509CertificateStructure.getInstance(seq));
         }
 
-        while ((line = readLine(in)) != null)
-        {
-            if (line.equals("-----END CERTIFICATE-----")
-                || line.equals("-----END X509 CERTIFICATE-----"))
-            {
-                break;
-            }
+        return null;
+    }
 
-            pemBuf.append(line);
-        }
+    protected CRL createCRL(CertificateList c)
+    throws CRLException
+    {
+        return new X509CRLObject(c);
+    }
+    
+    private CRL readPEMCRL(
+        InputStream  in)
+        throws IOException, CRLException
+    {
+        ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in);
 
-        if (pemBuf.length() != 0)
+        if (seq != null)
         {
-            return readDERCertificate(new ASN1InputStream(Base64.decode(pemBuf.toString())));
+            return createCRL(
+                            CertificateList.getInstance(seq));
         }
 
         return null;
     }
 
     private CRL readDERCRL(
-        ASN1InputStream dIn)
+        ASN1InputStream  aIn)
         throws IOException, CRLException
     {
-        return new X509CRLObject(new CertificateList((ASN1Sequence)dIn.readObject()));
-    }
-
-    private CRL readPEMCRL(
-        InputStream  in)
-        throws IOException, CRLException
-    {
-        String          line;
-        StringBuffer    pemBuf = new StringBuffer();
-
-        while ((line = readLine(in)) != null)
-        {
-            if (line.equals("-----BEGIN CRL-----")
-                || line.equals("-----BEGIN X509 CRL-----"))
-            {
-                break;
-            }
-        }
-
-        while ((line = readLine(in)) != null)
-        {
-            if (line.equals("-----END CRL-----")
-                || line.equals("-----END X509 CRL-----"))
-            {
-                break;
-            }
-
-            pemBuf.append(line);
-        }
-
-        if (pemBuf.length() != 0)
-        {
-            return readDERCRL(new ASN1InputStream(Base64.decode(pemBuf.toString())));
-        }
-
-        return null;
-    }
-
-    private CRL readPKCS7CRL(
-        InputStream  in)
-        throws IOException, CRLException
-    {
-        ASN1InputStream  dIn = new ASN1InputStream(in, getLimit(in));
-        ASN1Sequence     seq = (ASN1Sequence)dIn.readObject();
+        ASN1Sequence     seq = (ASN1Sequence)aIn.readObject();
 
         if (seq.size() > 1
                 && seq.getObjectAt(0) instanceof DERObjectIdentifier)
@@ -229,18 +137,29 @@
             if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
             {
                 sCrlData = new SignedData(ASN1Sequence.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true));
+                                (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs();
     
-                return new X509CRLObject(
-                            CertificateList.getInstance(
-                                    sCrlData.getCRLs().getObjectAt(sCrlDataObjectCount++)));
+                return getCRL();
             }
         }
 
-        return new X509CRLObject(
+        return createCRL(
                      CertificateList.getInstance(seq));
     }
 
+    private CRL getCRL()
+        throws CRLException
+    {
+        if (sCrlData == null || sCrlDataObjectCount >= sCrlData.size())
+        {
+            return null;
+        }
+
+        return createCRL(
+                            CertificateList.getInstance(
+                                    sCrlData.getObjectAt(sCrlDataObjectCount++)));
+    }
+
     /**
      * Generates a certificate object and initializes it with the data
      * read from the input stream inStream.
@@ -266,11 +185,9 @@
         {
             if (sData != null)
             {
-                if (sDataObjectCount != sData.getCertificates().size())
+                if (sDataObjectCount != sData.size())
                 {
-                    return new X509CertificateObject(
-                                X509CertificateStructure.getInstance(
-                                        sData.getCertificates().getObjectAt(sDataObjectCount++)));
+                    return getCertificate();
                 }
                 else
                 {
@@ -279,39 +196,31 @@
                     return null;
                 }
             }
-            
-            if (!in.markSupported())
-            {
-                in = new BufferedInputStream(in);
-            }
-            
-            in.mark(10);
-            int    tag = in.read();
-            
+
+            int limit = ProviderUtil.getReadLimit(in);
+
+            PushbackInputStream pis = new PushbackInputStream(in);
+            int tag = pis.read();
+
             if (tag == -1)
             {
                 return null;
             }
-            
+
+            pis.unread(tag);
+
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                in.reset();
-                return readPEMCertificate(in);
-            }
-            else if (in.read() == 0x80)    // assume BER encoded.
-            {
-                in.reset();
-                return readPKCS7Certificate(new ASN1InputStream(in, getLimit(in)));
+                return readPEMCertificate(pis);
             }
             else
             {
-                in.reset();
-                return readDERCertificate(new ASN1InputStream(in, getLimit(in)));
+                return readDERCertificate(new ASN1InputStream(pis, limit));
             }
         }
         catch (Exception e)
         {
-            throw new CertificateException(e.toString());
+            throw new CertificateException(e);
         }
     }
 
@@ -359,11 +268,9 @@
         {
             if (sCrlData != null)
             {
-                if (sCrlDataObjectCount != sCrlData.getCertificates().size())
+                if (sCrlDataObjectCount != sCrlData.size())
                 {
-                    return new X509CRLObject(
-                                CertificateList.getInstance(
-                                        sCrlData.getCRLs().getObjectAt(sCrlDataObjectCount++)));
+                    return getCRL();
                 }
                 else
                 {
@@ -372,27 +279,26 @@
                     return null;
                 }
             }
-            
-            if (!inStream.markSupported())
+
+            int limit = ProviderUtil.getReadLimit(inStream);
+
+            PushbackInputStream pis = new PushbackInputStream(inStream);
+            int tag = pis.read();
+
+            if (tag == -1)
             {
-                inStream = new BufferedInputStream(inStream);
+                return null;
             }
-            
-            inStream.mark(10);
-            if (inStream.read() != 0x30)  // assume ascii PEM encoded.
+
+            pis.unread(tag);
+
+            if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                inStream.reset();
-                return readPEMCRL(inStream);
-            }
-            else if (inStream.read() == 0x80)    // assume BER encoded.
-            {
-                inStream.reset();
-                return readPKCS7CRL(inStream);
+                return readPEMCRL(pis);
             }
             else
-            {
-                inStream.reset();
-                return readDERCRL(new ASN1InputStream(inStream, getLimit(inStream)));
+            {       // lazy evaluate to help processing of large CRLs
+                return readDERCRL(new ASN1InputStream(pis, limit, true));
             }
         }
         catch (CRLException e)
@@ -462,7 +368,7 @@
             {
                 if (!(obj instanceof X509Certificate))
                 {
-                    throw new CertificateException("list contains none X509Certificate object while creating CertPath\n" + obj.toString());
+                    throw new CertificateException("list contains non X509Certificate object while creating CertPath\n" + obj.toString());
                 }
             }
         }
diff --git a/src/main/java/org/bouncycastle/jce/provider/PBE.java b/src/main/java/org/bouncycastle/jce/provider/PBE.java
index 414076d..e06a27c 100644
--- a/src/main/java/org/bouncycastle/jce/provider/PBE.java
+++ b/src/main/java/org/bouncycastle/jce/provider/PBE.java
@@ -7,6 +7,9 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.PBEParametersGenerator;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// END android-removed
 import org.bouncycastle.crypto.digests.MD5Digest;
 // BEGIN android-removed
 // import org.bouncycastle.crypto.digests.RIPEMD160Digest;
@@ -34,6 +37,7 @@
     static final int        RIPEMD160   = 2;
     static final int        TIGER       = 3;
     static final int        SHA256      = 4;
+    static final int        MD2         = 5;
 
     static final int        PKCS5S1     = 0;
     static final int        PKCS5S2     = 1;
@@ -41,7 +45,7 @@
     static final int        OPENSSL     = 3;
 
     /**
-     * uses the appropriate mixer to generate the key and IV if neccessary.
+     * uses the appropriate mixer to generate the key and IV if necessary.
      */
     static class Util
     {
@@ -55,6 +59,11 @@
             {
                 switch (hash)
                 {
+                // BEGIN android-removed
+                // case MD2:
+                //     generator = new PKCS5S1ParametersGenerator(new MD2Digest());
+                //     break;
+                // END android-removed
                 case MD5:
                     generator = new PKCS5S1ParametersGenerator(new MD5Digest());
                     break;
@@ -62,7 +71,7 @@
                     generator = new PKCS5S1ParametersGenerator(new SHA1Digest());
                     break;
                 default:
-                    throw new IllegalStateException("PKCS5 scheme 1 only supports only MD5 and SHA1.");
+                    throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1.");
                 }
             }
             else if (type == PKCS5S2)
@@ -73,6 +82,11 @@
             {
                 switch (hash)
                 {
+                // BEGIN android-removed
+                // case MD2:
+                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
+                //     break;
+                // END android-removed
                 case MD5:
                     generator = new PKCS12ParametersGenerator(new MD5Digest());
                     break;
@@ -103,7 +117,7 @@
         }
 
         /**
-         * construct a key and iv (if neccessary) suitable for use with a 
+         * construct a key and iv (if necessary) suitable for use with a 
          * Cipher.
          */
         static CipherParameters makePBEParameters(
@@ -198,7 +212,7 @@
         }
     
         /**
-         * construct a key and iv (if neccessary) suitable for use with a 
+         * construct a key and iv (if necessary) suitable for use with a 
          * Cipher.
          */
         static CipherParameters makePBEParameters(
diff --git a/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java b/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java
new file mode 100644
index 0000000..6f33cfe
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class PEMUtil
+{
+    private final String _header1;
+    private final String _header2;
+    private final String _footer1;
+    private final String _footer2;
+
+    PEMUtil(
+        String type)
+    {
+        _header1 = "-----BEGIN " + type + "-----";
+        _header2 = "-----BEGIN X509 " + type + "-----";
+        _footer1 = "-----END " + type + "-----";
+        _footer2 = "-----END X509 " + type + "-----";
+    }
+
+    private String readLine(
+        InputStream in)
+        throws IOException
+    {
+        int             c;
+        StringBuffer    l = new StringBuffer();
+
+        do
+        {
+            while (((c = in.read()) != '\r') && c != '\n' && (c >= 0))
+            {
+                if (c == '\r')
+                {
+                    continue;
+                }
+
+                l.append((char)c);
+            }
+        }
+        while (c >= 0 && l.length() == 0);
+
+        if (c < 0)
+        {
+            return null;
+        }
+
+        return l.toString();
+    }
+
+    ASN1Sequence readPEMObject(
+        InputStream  in)
+        throws IOException
+    {
+        String          line;
+        StringBuffer    pemBuf = new StringBuffer();
+
+        while ((line = readLine(in)) != null)
+        {
+            if (line.startsWith(_header1) || line.startsWith(_header2))
+            {
+                break;
+            }
+        }
+
+        while ((line = readLine(in)) != null)
+        {
+            if (line.startsWith(_footer1) || line.startsWith(_footer2))
+            {
+                break;
+            }
+
+            pemBuf.append(line);
+        }
+
+        if (pemBuf.length() != 0)
+        {
+            DERObject o = new ASN1InputStream(Base64.decode(pemBuf.toString())).readObject();
+            if (!(o instanceof ASN1Sequence))
+            {
+                throw new IOException("malformed PEM data encountered");
+            }
+
+            return (ASN1Sequence)o;
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java b/src/main/java/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java
new file mode 100644
index 0000000..4e77119
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+// BEGIN android-added
+import org.bouncycastle.asn1.OrderedTable;
+// END android-added
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1InputStream;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.io.ObjectOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+class PKCS12BagAttributeCarrierImpl
+    implements PKCS12BagAttributeCarrier
+{
+    // BEGIN android-changed
+    private OrderedTable pkcs12 = new OrderedTable();
+    // END android-changed
+
+    // BEGIN android-removed
+    // PKCS12BagAttributeCarrierImpl(Hashtable attributes, Vector ordering)
+    // {
+    //     this.pkcs12Attributes = attributes;
+    //     this.pkcs12Ordering = ordering;
+    // }
+    // END android-removed
+
+    public PKCS12BagAttributeCarrierImpl()
+    {
+        // BEGIN android-removed
+        // this(new Hashtable(), new Vector());
+        // END android-removed
+    }
+
+    public void setBagAttribute(
+        DERObjectIdentifier oid,
+        DEREncodable        attribute)
+    {
+        // BEGIN android-changed
+        // preserve original ordering
+        pkcs12.put(oid, attribute);
+        // END android-changed
+    }
+
+    public DEREncodable getBagAttribute(
+        DERObjectIdentifier oid)
+    {
+        // BEGIN android-changed
+        return (DEREncodable)pkcs12.get(oid);
+        // END android-changed
+    }
+
+    public Enumeration getBagAttributeKeys()
+    {
+        // BEGIN android-changed
+        return pkcs12.getKeys();
+        // END android-changed
+    }
+
+    int size()
+    {
+        // BEGIN android-changed
+        return pkcs12.size();
+        // END android-changed
+    }
+
+    // BEGIN android-removed
+    // Hashtable getAttributes()
+    // {
+    //     return pkcs12Attributes;
+    // }
+    //
+    // Vector getOrdering()
+    // {
+    //     return pkcs12Ordering;
+    // }
+    // END android-removed
+
+    public void writeObject(ObjectOutputStream out)
+        throws IOException
+    {
+        if (pkcs12.size() == 0)
+        {
+            out.writeObject(new Hashtable());
+            out.writeObject(new Vector());
+        }
+        else
+        {
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+            ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+            Enumeration             e = this.getBagAttributeKeys();
+
+            while (e.hasMoreElements())
+            {
+                DERObjectIdentifier    oid = (DERObjectIdentifier)e.nextElement();
+
+                aOut.writeObject(oid);
+                aOut.writeObject(pkcs12.get(oid));
+            }
+
+            out.writeObject(bOut.toByteArray());
+        }
+    }
+
+    public void readObject(ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        Object obj = in.readObject();
+
+        if (obj instanceof Hashtable)
+        {
+            // BEGIN android-changed
+            // we only write out Hashtable/Vector in empty case
+            in.readObject(); // consume empty Vector
+            this.pkcs12 = new OrderedTable();
+            // END android-changed
+        }
+        else
+        {
+            ASN1InputStream aIn = new ASN1InputStream((byte[])obj);
+
+            DERObjectIdentifier    oid;
+
+            while ((oid = (DERObjectIdentifier)aIn.readObject()) != null)
+            {
+                this.setBagAttribute(oid, aIn.readObject());
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPath.java b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPath.java
index 69585b8..8c8969a 100644
--- a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPath.java
+++ b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPath.java
@@ -28,7 +28,6 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.pkcs.ContentInfo;
@@ -94,7 +93,8 @@
         
         // find end-entity cert
         List       retList = new ArrayList(certs.size());
-        
+        List       orig = new ArrayList(certs);
+
         for (int i = 0; i < certs.size(); i++)
         {
             X509Certificate cert = (X509Certificate)certs.get(i);
@@ -122,12 +122,7 @@
         // can only have one end entity cert - something's wrong, give up.
         if (retList.size() > 1)
         {
-            for (int i = 0; i != certs.size(); i++)
-            {
-                retList.add(certs.get(i));
-            }
-            
-            return retList;
+            return orig;
         }
 
         for (int i = 0; i != retList.size(); i++)
@@ -147,9 +142,9 @@
         }
         
         // make sure all certificates are accounted for.
-        for (int i = 0; i != certs.size(); i++)
+        if (certs.size() > 0)
         {
-            retList.add(certs.get(i));
+            return orig;
         }
         
         return retList;
@@ -183,21 +178,14 @@
                     throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
                 }
                 Enumeration e = ((ASN1Sequence)derObject).getObjects();
-                InputStream certInStream;
-                ByteArrayOutputStream outStream;
-                DEROutputStream derOutStream;
                 certificates = new ArrayList();
-                CertificateFactory certFactory= CertificateFactory.getInstance("X.509", "BC");
+                CertificateFactory certFactory = CertificateFactory.getInstance("X.509", "BC");
                 while (e.hasMoreElements())
                 {
-                    outStream = new ByteArrayOutputStream();
-                    derOutStream = new DEROutputStream(outStream);
-        
-                    derOutStream.writeObject(e.nextElement());
-                    derOutStream.close();
-    
-                    certInStream = new ByteArrayInputStream(outStream.toByteArray());
-                    certificates.add(0,certFactory.generateCertificate(certInStream));
+                    ASN1Encodable element = (ASN1Encodable)e.nextElement();
+                    byte[] encoded = element.getEncoded(ASN1Encodable.DER);
+                    certificates.add(0, certFactory.generateCertificate(
+                        new ByteArrayInputStream(encoded)));
                 }
             }
             else if (encoding.equalsIgnoreCase("PKCS7") || encoding.equalsIgnoreCase("PEM"))
@@ -375,17 +363,11 @@
     {
         try
         {
-            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-            DEROutputStream       dOut = new DEROutputStream(bOut);
-            
-            dOut.writeObject(obj);
-            dOut.close();
-            
-            return bOut.toByteArray();
+            return obj.getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
-            throw new CertificateEncodingException("Exeption thrown: " + e);
+            throw new CertificateEncodingException("Exception thrown: " + e);
         }
     }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
index 577e5ca..05bba8e 100644
--- a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
+++ b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
@@ -1,76 +1,162 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.IOException;
+import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.X509CertStoreSelector;
+
 import java.security.InvalidAlgorithmParameterException;
-import java.security.PublicKey;
-import java.security.cert.*;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertPathBuilderSpi;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathBuilderResult;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
-
-import javax.security.auth.x500.X500Principal;
 
 /**
- * Implements the PKIX CertPathBuilding algorithem for BouncyCastle.
- * <br />
- * <b>MAYBE: implement more CertPath validation whil build path to omit invalid pathes</b>
- *
+ * Implements the PKIX CertPathBuilding algorithm for BouncyCastle.
+ * 
  * @see CertPathBuilderSpi
- **/
+ */
 public class PKIXCertPathBuilderSpi
     extends CertPathBuilderSpi
 {
     /**
      * Build and validate a CertPath using the given parameter.
-     *
-     * @param params PKIXBuilderParameters object containing all
-     * information to build the CertPath
-     **/
-    public CertPathBuilderResult engineBuild(
-        CertPathParameters params)
-        throws CertPathBuilderException, InvalidAlgorithmParameterException 
+     * 
+     * @param params PKIXBuilderParameters object containing all information to
+     *            build the CertPath
+     */
+    public CertPathBuilderResult engineBuild(CertPathParameters params)
+        throws CertPathBuilderException, InvalidAlgorithmParameterException
     {
-        if (!(params instanceof PKIXBuilderParameters))
+        if (!(params instanceof PKIXBuilderParameters)
+            && !(params instanceof ExtendedPKIXBuilderParameters))
         {
-            throw new InvalidAlgorithmParameterException("params must be a PKIXBuilderParameters instance");
+            throw new InvalidAlgorithmParameterException(
+                "Parameters must be an instance of "
+                    + PKIXBuilderParameters.class.getName() + " or "
+                    + ExtendedPKIXBuilderParameters.class.getName() + ".");
         }
 
-        PKIXBuilderParameters pkixParams = (PKIXBuilderParameters)params;
+        ExtendedPKIXBuilderParameters pkixParams = null;
+        if (params instanceof ExtendedPKIXBuilderParameters)
+        {
+            pkixParams = (ExtendedPKIXBuilderParameters) params;
+        }
+        else
+        {
+            pkixParams = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters
+                .getInstance((PKIXBuilderParameters) params);
+        }
 
         Collection targets;
         Iterator targetIter;
         List certPathList = new ArrayList();
         X509Certificate cert;
-        Collection      certs;
-        CertPath        certPath = null;
-        Exception       certPathException = null;
 
         // search target certificates
-        CertSelector certSelect = pkixParams.getTargetCertConstraints();
-        if (certSelect == null)
+
+        Selector certSelect = pkixParams.getTargetConstraints();
+        if (!(certSelect instanceof X509CertStoreSelector))
         {
-            throw new CertPathBuilderException("targetCertConstraints must be non-null for CertPath building");
+            throw new CertPathBuilderException(
+                "TargetConstraints must be an instance of "
+                    + X509CertStoreSelector.class.getName() + " for "
+                    + this.getClass().getName() + " class.");
         }
 
         try
         {
-            targets = findCertificates(certSelect, pkixParams.getCertStores());
+            targets = CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getStores());
+            targets.addAll(CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getCertStores()));
         }
-        catch (CertStoreException e)
+        catch (AnnotatedException e)
         {
-            throw new CertPathBuilderException(e);
+            throw new ExtCertPathBuilderException(
+                "Error finding target certificate.", e);
         }
 
         if (targets.isEmpty())
         {
-            throw new CertPathBuilderException("no certificate found matching targetCertContraints");
+
+            throw new CertPathBuilderException(
+                "No certificate found matching targetContraints.");
         }
 
-        CertificateFactory  cFact;
-        CertPathValidator   validator;
+        CertPathBuilderResult result = null;
+
+        // check all potential target certificates
+        targetIter = targets.iterator();
+        while (targetIter.hasNext() && result == null)
+        {
+            cert = (X509Certificate) targetIter.next();
+            result = build(cert, pkixParams, certPathList);
+        }
+
+        if (result == null && certPathException != null)
+        {
+            if (certPathException instanceof AnnotatedException)
+            {
+                throw new CertPathBuilderException(certPathException.getMessage(), certPathException.getCause());
+            }
+            throw new CertPathBuilderException(
+                "Possible certificate chain could not be validated.",
+                certPathException);
+        }
+
+        if (result == null && certPathException == null)
+        {
+            throw new CertPathBuilderException(
+                "Unable to find certificate chain.");
+        }
+
+        return result;
+    }
+
+    private Exception certPathException;
+
+    protected CertPathBuilderResult build(X509Certificate tbvCert,
+        ExtendedPKIXBuilderParameters pkixParams, List tbvPath)
+    {
+        // If tbvCert is readily present in tbvPath, it indicates having run
+        // into a cycle in the
+        // PKI graph.
+        if (tbvPath.contains(tbvCert))
+        {
+            return null;
+        }
+        // step out, the certificate is not allowed to appear in a certification
+        // chain.
+        if (pkixParams.getExcludedCerts().contains(tbvCert))
+        {
+            return null;
+        }
+        // test if certificate path exceeds maximum length
+        if (pkixParams.getMaxPathLength() != -1)
+        {
+            if (tbvPath.size() - 1 > pkixParams.getMaxPathLength())
+            {
+                return null;
+            }
+        }
+
+        tbvPath.add(tbvCert);
+
+        CertificateFactory cFact;
+        CertPathValidator validator;
+        CertPathBuilderResult builderResult = null;
 
         try
         {
@@ -79,275 +165,98 @@
         }
         catch (Exception e)
         {
-            throw new CertPathBuilderException("exception creating support classes: " + e);
+            // cannot happen
+            throw new RuntimeException("Exception creating support classes.");
         }
 
-        //
-        // check all potential target certificates
-        targetIter = targets.iterator();
-        while (targetIter.hasNext())
-        {
-            cert = (X509Certificate)targetIter.next();
-            certPathList.clear();
-            while (cert != null)
-            {
-                // add cert to the certpath
-                certPathList.add(cert);
-
-                // check wether the issuer of <cert> is a TrustAnchor 
-                if (findTrustAnchor(cert, pkixParams.getTrustAnchors()) != null)
-                {
-                    try
-                    {
-                        certPath = cFact.generateCertPath(certPathList);
-
-                        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPath, pkixParams);
-
-                        return new PKIXCertPathBuilderResult(certPath,
-                                     result.getTrustAnchor(),
-                                     result.getPolicyTree(),
-                                     result.getPublicKey());
-                    }
-                    catch (CertificateException ex)
-                    {
-                        certPathException = ex;
-                    }
-                    catch (CertPathValidatorException ex)
-                    {
-                        certPathException = ex;
-                    }
-                    // if validation failed go to next certificate
-                    cert = null;
-                }
-                else
-                {
-                    // try to get the issuer certificate from one
-                    // of the CertStores
-                    try
-                    {
-                        X509Certificate issuer = findIssuer(cert, pkixParams.getCertStores());
-                        if (issuer.equals(cert))
-                        {
-                            cert = null;
-                        }
-                        else
-                        {
-                            cert = issuer;
-                        }
-                    }
-                    catch (CertPathValidatorException ex)
-                    {
-                        certPathException = ex;
-                        cert = null;
-                    }
-                }
-            }
-        }
-
-        if (certPath != null)
-        {
-            throw new CertPathBuilderException("found certificate chain, but could not be validated", certPathException);
-        }
-
-        throw new CertPathBuilderException("unable to find certificate chain");
-    }
-
-    /**
-     * Search the given Set of TrustAnchor's for one that is the
-     * issuer of the fiven X509 certificate.
-     *
-     * @param cert the X509 certificate
-     * @param trustAnchors a Set of TrustAnchor's
-     *
-     * @return the <code>TrustAnchor</code> object if found or
-     * <code>null</code> if not.
-     *
-     * @exception CertPathValidatorException if a TrustAnchor  was
-     * found but the signature verificytion on the given certificate
-     * has thrown an exception. This Exception can be obtainted with
-     * <code>getCause()</code> method.
-     **/
-    final TrustAnchor findTrustAnchor(
-        X509Certificate cert,
-        Set             trustAnchors) 
-        throws CertPathBuilderException
-    {
-        Iterator iter = trustAnchors.iterator();
-        TrustAnchor trust = null;
-        PublicKey trustPublicKey = null;
-        Exception invalidKeyEx = null;
-
-        X509CertSelector certSelectX509 = new X509CertSelector();
-
         try
         {
-            certSelectX509.setSubject(cert.getIssuerX500Principal().getEncoded());
-        }
-        catch (IOException ex)
-        {
-            throw new CertPathBuilderException("can't get trust anchor principal",null);
-        }
-
-        while (iter.hasNext() && trust == null)
-        {
-            trust = (TrustAnchor)iter.next();
-            if (trust.getTrustedCert() != null)
+            // check whether the issuer of <tbvCert> is a TrustAnchor
+            // BEGIN android-changed
+            if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams) != null)
+                // END android-changed
             {
-                if (certSelectX509.match(trust.getTrustedCert()))
-                {
-                    trustPublicKey = trust.getTrustedCert().getPublicKey();
-                }
-                else
-                {
-                    trust = null;
-                }
-            }
-            else if (trust.getCAName() != null
-                        && trust.getCAPublicKey() != null)
-            {
+                // exception message from possibly later tried certification
+                // chains
+                CertPath certPath = null;
+                PKIXCertPathValidatorResult result = null;
                 try
                 {
-                    X500Principal certIssuer = cert.getIssuerX500Principal();
-                    X500Principal caName = new X500Principal(trust.getCAName());
-                    if (certIssuer.equals(caName))
-                    {
-                        trustPublicKey = trust.getCAPublicKey();
-                    }
-                    else
-                    {
-                        trust = null;
-                    }
+                    certPath = cFact.generateCertPath(tbvPath);
                 }
-                catch (IllegalArgumentException ex)
+                catch (Exception e)
                 {
-                    trust = null;
+                    throw new AnnotatedException(
+                        "Certification path could not be constructed from certificate list.",
+                        e);
                 }
+
+                try
+                {
+                    result = (PKIXCertPathValidatorResult) validator.validate(
+                        certPath, pkixParams);
+                }
+                catch (Exception e)
+                {
+                    throw new AnnotatedException(
+                        "Certification path could not be validated.", e);
+                }
+
+                return new PKIXCertPathBuilderResult(certPath, result
+                    .getTrustAnchor(), result.getPolicyTree(), result
+                    .getPublicKey());
+
             }
             else
             {
-                trust = null;
-            }
-            
-            if (trustPublicKey != null)
-            {
+                // add additional X.509 stores from locations in certificate
                 try
                 {
-                    cert.verify(trustPublicKey);
+                    CertPathValidatorUtilities.addAdditionalStoresFromAltNames(
+                        tbvCert, pkixParams);
                 }
-                catch (Exception ex)
+                catch (CertificateParsingException e)
                 {
-                    invalidKeyEx = ex;
-                    trust = null;
+                    throw new AnnotatedException(
+                        "No additiontal X.509 stores can be added from certificate locations.",
+                        e);
+                }
+                Collection issuers = new HashSet();
+                // try to get the issuer certificate from one
+                // of the stores
+                try
+                {
+                    issuers.addAll(CertPathValidatorUtilities.findIssuerCerts(tbvCert, pkixParams));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException(
+                        "Cannot find issuer certificate for certificate in certification path.",
+                        e);
+                }
+                if (issuers.isEmpty())
+                {
+                    throw new AnnotatedException(
+                        "No issuer certificate for certificate in certification path found.");
+                }
+                Iterator it = issuers.iterator();
+
+                while (it.hasNext() && builderResult == null)
+                {
+                    X509Certificate issuer = (X509Certificate) it.next();
+                    builderResult = build(issuer, pkixParams, tbvPath);
                 }
             }
         }
-    
-        if (trust == null && invalidKeyEx != null)
+        catch (AnnotatedException e)
         {
-            throw new CertPathBuilderException("TrustAnchor found put certificate validation failed",invalidKeyEx);
+            certPathException = e;
         }
-
-        return trust;
+        if (builderResult == null)
+        {
+            tbvPath.remove(tbvCert);
+        }
+        return builderResult;
     }
 
-    /**
-     * Return a Collection of all certificates found in the
-     * CertStore's that are matching the certSelect criteriums.
-     *
-     * @param certSelector a {@link CertSelector CertSelector}
-     * object that will be used to select the certificates
-     * @param certStores a List containing only {@link CertStore
-     * CertStore} objects. These are used to search for
-     * certificates
-     *
-     * @return a Collection of all found {@link Certificate Certificate}
-     * objects. May be empty but never <code>null</code>.
-     **/
-    private final Collection findCertificates(
-        CertSelector    certSelect,
-        List            certStores) 
-        throws CertStoreException
-    {
-        Set certs = new HashSet();
-        Iterator iter = certStores.iterator();
-
-        while (iter.hasNext())
-        {
-            CertStore   certStore = (CertStore)iter.next();
-
-            certs.addAll(certStore.getCertificates(certSelect));
-        }
-
-        return certs;
-    }
-    
-    /**
-     * Find the issuer certificate of the given certificate.
-     *
-     * @param cert the certificate hows issuer certificate should
-     * be found.
-     * @param certStores a list of <code>CertStore</code> object
-     * that will be searched
-     *
-     * @return then <code>X509Certificate</code> object containing
-     * the issuer certificate or <code>null</code> if not found
-     *
-     * @exception CertPathValidatorException if a TrustAnchor  was
-     * found but the signature verificytion on the given certificate
-     * has thrown an exception. This Exception can be obtainted with
-     * <code>getCause()</code> method.
-     **/
-    private final X509Certificate findIssuer(
-        X509Certificate cert,
-        List certStores)
-        throws CertPathValidatorException
-    {
-        Exception invalidKeyEx = null;
-        X509CertSelector certSelect = new X509CertSelector();
-        try
-        {
-            certSelect.setSubject(cert.getIssuerX500Principal().getEncoded());
-        }
-        catch (IOException ex)
-        {
-            throw new CertPathValidatorException("Issuer not found", null, null, -1);
-        }
-
-        Iterator iter;
-        try
-        {
-            iter = findCertificates(certSelect, certStores).iterator();
-        }
-        catch (CertStoreException e)
-        {
-            throw new CertPathValidatorException(e);
-        }
-        
-        X509Certificate issuer = null;
-        while (iter.hasNext() && issuer == null)
-        {
-            issuer = (X509Certificate)iter.next();
-            try
-            {
-                cert.verify(issuer.getPublicKey());
-            }
-            catch (Exception ex)
-            {
-                invalidKeyEx = ex;
-                issuer = null;
-            }
-        }
-
-        if (issuer == null && invalidKeyEx == null)
-        {
-           throw new CertPathValidatorException("Issuer not found", null, null, -1);
-        }
-
-        if (issuer == null && invalidKeyEx != null)
-        {
-            throw new CertPathValidatorException("issuer found but certificate validation failed",invalidKeyEx,null,-1);
-        }
-
-        return issuer;
-    }
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
index f5d6711..54b0d20 100644
--- a/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
+++ b/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
@@ -10,103 +7,59 @@
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertPathValidatorResult;
 import java.security.cert.CertPathValidatorSpi;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.PKIXCertPathChecker;
 import java.security.cert.PKIXCertPathValidatorResult;
 import java.security.cert.PKIXParameters;
 import java.security.cert.TrustAnchor;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLEntry;
-import java.security.cert.X509CRLSelector;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DEREnumerated;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralSubtree;
-import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
-import org.bouncycastle.asn1.x509.NameConstraints;
-import org.bouncycastle.asn1.x509.PolicyInformation;
-import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
 
 /**
- * CertPathValidatorSpi implemenation for X.509 Certificate validation ala rfc 3280<br />
- **/
-public class PKIXCertPathValidatorSpi extends CertPathValidatorSpi
+ * CertPathValidatorSpi implementation for X.509 Certificate validation � la RFC
+ * 3280.
+ */
+public class PKIXCertPathValidatorSpi
+        extends CertPathValidatorSpi
 {
-    private static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId();
-    private static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId();
-    private static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId();
-    private static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId();
-    private static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId();
-    private static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId();
-    private static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId();
-    private static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId();
-    private static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId();
-    private static final String KEY_USAGE = X509Extensions.KeyUsage.getId();
 
-    private static final String CRL_NUMBER = X509Extensions.CRLNumber.getId();
-
-    private static final String ANY_POLICY = "2.5.29.32.0";
-
-
-    /*
-     * key usage bits
-     */
-    private static final int    KEY_CERT_SIGN = 5;
-    private static final int    CRL_SIGN = 6;
-
-    private static final String[] crlReasons = new String[] {
-                                        "unspecified",
-                                        "keyCompromise",
-                                        "cACompromise",
-                                        "affiliationChanged",
-                                        "superseded",
-                                        "cessationOfOperation",
-                                        "certificateHold",
-                                        "unknown",
-                                        "removeFromCRL",
-                                        "privilegeWithdrawn",
-                                        "aACompromise" };
-    
-    
     public CertPathValidatorResult engineValidate(
-        CertPath certPath,
-        CertPathParameters params)
-        throws CertPathValidatorException, InvalidAlgorithmParameterException
+            CertPath certPath,
+            CertPathParameters params)
+            throws CertPathValidatorException,
+            InvalidAlgorithmParameterException
     {
         if (!(params instanceof PKIXParameters))
         {
-            throw new InvalidAlgorithmParameterException("params must be a PKIXParameters instance");
+            throw new InvalidAlgorithmParameterException("Parameters must be a " + PKIXParameters.class.getName()
+                    + " instance.");
         }
 
-        PKIXParameters paramsPKIX = (PKIXParameters)params;
+        ExtendedPKIXParameters paramsPKIX;
+        if (params instanceof ExtendedPKIXParameters)
+        {
+            paramsPKIX = (ExtendedPKIXParameters)params;
+        }
+        else
+        {
+            paramsPKIX = ExtendedPKIXParameters.getInstance((PKIXParameters)params);
+        }
         if (paramsPKIX.getTrustAnchors() == null)
         {
-            throw new InvalidAlgorithmParameterException("trustAnchors is null, this is not allowed for path validation");
+            throw new InvalidAlgorithmParameterException(
+                    "trustAnchors is null, this is not allowed for certification path validation.");
         }
 
         //
@@ -116,18 +69,18 @@
         //
         // (a)
         //
-        List    certs = certPath.getCertificates();
-        int     n = certs.size();
-        
+        List certs = certPath.getCertificates();
+        int n = certs.size();
+
         if (certs.isEmpty())
         {
-            throw new CertPathValidatorException("CertPath is empty", null, certPath, 0);
+            throw new CertPathValidatorException("Certification path is empty.", null, certPath, 0);
         }
 
         //
         // (b)
         //
-        Date validDate = CertPathValidatorUtilities.getValidDate(paramsPKIX);
+        // Date validDate = CertPathValidatorUtilities.getValidDate(paramsPKIX);
 
         //
         // (c)
@@ -137,26 +90,34 @@
         //
         // (d)
         // 
-        X509Certificate lastCert = (X509Certificate)certs.get(certs.size() - 1);
-
-// BEGIN android-changed
-        TrustAnchor trust = CertPathValidatorUtilities.findTrustAnchor(lastCert,
-                certPath, certs.size() - 1, paramsPKIX);
-// END android-changed
+        TrustAnchor trust;
+        // BEGIN android-added
+        X509Certificate lastCert = (X509Certificate) certs.get(certs.size() - 1);
+        // END android-added
+        try
+        {
+            // BEGIN android-changed
+            trust = CertPathValidatorUtilities.findTrustAnchor(lastCert, paramsPKIX);
+            // END android-changed
+        }
+        catch (AnnotatedException e)
+        {
+            throw new CertPathValidatorException(e.getMessage(), e, certPath, certs.size() - 1);
+        }
 
         if (trust == null)
         {
-            throw new CertPathValidatorException("TrustAnchor for CertPath not found.", null, certPath, -1);
+            throw new CertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
         }
-        
+
         //
         // (e), (f), (g) are part of the paramsPKIX object.
         //
-
         Iterator certIter;
         int index = 0;
         int i;
-
+        // Certificate for each interation of the validation loop
+        // Signature information for each iteration of the validation loop
         //
         // 6.1.2 - setup
         //
@@ -164,7 +125,7 @@
         //
         // (a)
         //
-        List     []  policyNodes = new ArrayList[n + 1];
+        List[] policyNodes = new ArrayList[n + 1];
         for (int j = 0; j < policyNodes.length; j++)
         {
             policyNodes[j] = new ArrayList();
@@ -172,31 +133,22 @@
 
         Set policySet = new HashSet();
 
-        policySet.add(ANY_POLICY);
+        policySet.add(RFC3280CertPathUtilities.ANY_POLICY);
 
-        PKIXPolicyNode  validPolicyTree = new PKIXPolicyNode(new ArrayList(), 0, policySet, null, new HashSet(), ANY_POLICY, false);
+        PKIXPolicyNode validPolicyTree = new PKIXPolicyNode(new ArrayList(), 0, policySet, null, new HashSet(),
+                RFC3280CertPathUtilities.ANY_POLICY, false);
 
         policyNodes[0].add(validPolicyTree);
 
         //
-        // (b)
+        // (b) and (c)
         //
-        Set     permittedSubtreesDN = new HashSet();
-        Set     permittedSubtreesEmail = new HashSet();
-        Set     permittedSubtreesIP = new HashSet();
-    
-        //
-        // (c)
-        //
-        Set     excludedSubtreesDN = new HashSet();
-        Set     excludedSubtreesEmail = new HashSet();
-        Set     excludedSubtreesIP = new HashSet();
-    
-        //
+        PKIXNameConstraintValidator nameConstraintValidator = new PKIXNameConstraintValidator();
+
         // (d)
         //
         int explicitPolicy;
-        Set acceptablePolicies = null;
+        Set acceptablePolicies = new HashSet();
 
         if (paramsPKIX.isExplicitPolicyRequired())
         {
@@ -220,7 +172,7 @@
         {
             inhibitAnyPolicy = n + 1;
         }
-    
+
         //
         // (f)
         //
@@ -234,7 +186,7 @@
         {
             policyMapping = n + 1;
         }
-    
+
         //
         // (g), (h), (i), (j)
         //
@@ -242,14 +194,16 @@
         X500Principal workingIssuerName;
 
         X509Certificate sign = trust.getTrustedCert();
+        // BEGIN android-added
         boolean trustAnchorInChain = false;
+        // END android-added
         try
         {
             if (sign != null)
             {
                 workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
                 workingPublicKey = sign.getPublicKey();
-
+                // BEGIN android-added
                 // There is similar code in CertPathValidatorUtilities.
                 try {
                     byte[] trustBytes = sign.getEncoded();
@@ -258,6 +212,7 @@
                 } catch(Exception e) {
                     // ignore, continue with trustAnchorInChain being false
                 }
+                // END android-added
             }
             else
             {
@@ -267,12 +222,22 @@
         }
         catch (IllegalArgumentException ex)
         {
-            throw new CertPathValidatorException("TrustAnchor subjectDN: " + ex.toString());
+            throw new ExtCertPathValidatorException("Subject of trust anchor could not be (re)encoded.", ex, certPath,
+                    -1);
         }
 
-        AlgorithmIdentifier workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey);
+        AlgorithmIdentifier workingAlgId = null;
+        try
+        {
+            workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey);
+        }
+        catch (CertPathValidatorException e)
+        {
+            throw new ExtCertPathValidatorException(
+                    "Algorithm identifier of public key of trust anchor could not be read.", e, certPath, -1);
+        }
         DERObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.getObjectId();
-        DEREncodable        workingPublicKeyParameters = workingAlgId.getParameters();
+        DEREncodable workingPublicKeyParameters = workingAlgId.getParameters();
 
         //
         // (k)
@@ -282,1187 +247,214 @@
         //
         // 6.1.3
         //
-        Iterator tmpIter;
-        int tmpInt;
 
-        if (paramsPKIX.getTargetCertConstraints() != null
-            && !paramsPKIX.getTargetCertConstraints().match((X509Certificate)certs.get(0)))
+        if (paramsPKIX.getTargetConstraints() != null
+                && !paramsPKIX.getTargetConstraints().match((X509Certificate) certs.get(0)))
         {
-            throw new CertPathValidatorException("target certificate in certpath does not match targetcertconstraints", null, certPath, 0);
+            throw new ExtCertPathValidatorException(
+                    "Target certificate in certification path does not match targetConstraints.", null, certPath, 0);
         }
 
-
         // 
-        // initialise CertPathChecker's
+        // initialize CertPathChecker's
         //
-        List  pathCheckers = paramsPKIX.getCertPathCheckers();
+        List pathCheckers = paramsPKIX.getCertPathCheckers();
         certIter = pathCheckers.iterator();
         while (certIter.hasNext())
         {
-            ((PKIXCertPathChecker)certIter.next()).init(false);
+            ((PKIXCertPathChecker) certIter.next()).init(false);
         }
 
         X509Certificate cert = null;
 
-        for (index = certs.size() - 1; index >= 0 ; index--)
+        for (index = certs.size() - 1; index >= 0; index--)
         {
-            try
+            // try
+            // {
+            //
+            // i as defined in the algorithm description
+            //
+            i = n - index;
+
+            //
+            // set certificate to be checked in this round
+            // sign and workingPublicKey and workingIssuerName are set
+            // at the end of the for loop and initialized the
+            // first time from the TrustAnchor
+            //
+            cert = (X509Certificate) certs.get(index);
+            boolean verificationAlreadyPerformed = (index == certs.size() - 1);
+
+            //
+            // 6.1.3
+            //
+
+            // BEGIN android-changed
+            RFC3280CertPathUtilities.processCertA(certPath, paramsPKIX, index, workingPublicKey,
+                verificationAlreadyPerformed, workingIssuerName, sign, i, trustAnchorInChain);
+            // END android-changed
+
+            RFC3280CertPathUtilities.processCertBC(certPath, index, nameConstraintValidator);
+
+            validPolicyTree = RFC3280CertPathUtilities.processCertD(certPath, index, acceptablePolicies,
+                    validPolicyTree, policyNodes, inhibitAnyPolicy);
+
+            validPolicyTree = RFC3280CertPathUtilities.processCertE(certPath, index, validPolicyTree);
+
+            RFC3280CertPathUtilities.processCertF(certPath, index, validPolicyTree, explicitPolicy);
+
+            //
+            // 6.1.4
+            //
+
+            if (i != n)
             {
-                //
-                // i as defined in the algorithm description
-                //
-                i = n - index;
-    
-                //
-                // set certificate to be checked in this round
-                // sign and workingPublicKey and workingIssuerName are set
-                // at the end of the for loop and initialied the
-                // first time from the TrustAnchor
-                //
-                cert = (X509Certificate)certs.get(index);
-    
-                //
-                // 6.1.3
-                //
-    
-                //
-                // (a) verify
-                //
-                try
+                // BEGIN android-added
+                if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
                 {
-                    // (a) (1)
-                    //
-                    if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
-                    {
-                        cert.verify(workingPublicKey, "BC");
-                    }
+                // END android-added
+                if (cert != null && cert.getVersion() == 1)
+                {
+                    throw new CertPathValidatorException("Version 1 certificates can't be used as CA ones.", null,
+                            certPath, index);
                 }
-                catch (GeneralSecurityException e)
-                {
-                    throw new CertPathValidatorException("Could not validate certificate signature.", e, certPath, index);
+                // BEGIN android-added
                 }
-    
-                try
+                // END android-added
+
+                RFC3280CertPathUtilities.prepareNextCertA(certPath, index);
+
+                validPolicyTree = RFC3280CertPathUtilities.prepareCertB(certPath, index, policyNodes, validPolicyTree,
+                        policyMapping);
+
+                RFC3280CertPathUtilities.prepareNextCertG(certPath, index, nameConstraintValidator);
+
+                // (h)
+                explicitPolicy = RFC3280CertPathUtilities.prepareNextCertH1(certPath, index, explicitPolicy);
+                policyMapping = RFC3280CertPathUtilities.prepareNextCertH2(certPath, index, policyMapping);
+                inhibitAnyPolicy = RFC3280CertPathUtilities.prepareNextCertH3(certPath, index, inhibitAnyPolicy);
+
+                //
+                // (i)
+                //
+                explicitPolicy = RFC3280CertPathUtilities.prepareNextCertI1(certPath, index, explicitPolicy);
+                policyMapping = RFC3280CertPathUtilities.prepareNextCertI2(certPath, index, policyMapping);
+
+                // (j)
+                inhibitAnyPolicy = RFC3280CertPathUtilities.prepareNextCertJ(certPath, index, inhibitAnyPolicy);
+
+                // (k)
+                // BEGIN android-changed
+                RFC3280CertPathUtilities.prepareNextCertK(certPath, index, i, trustAnchorInChain);
+                // END android-changed
+
+                // (l)
+                maxPathLength = RFC3280CertPathUtilities.prepareNextCertL(certPath, index, maxPathLength);
+
+                // (m)
+                maxPathLength = RFC3280CertPathUtilities.prepareNextCertM(certPath, index, maxPathLength);
+
+                // (n)
+                RFC3280CertPathUtilities.prepareNextCertN(certPath, index);
+
+                Set criticalExtensions = cert.getCriticalExtensionOIDs();
+                if (criticalExtensions != null)
                 {
-                    // (a) (2)
-                    //
-                    cert.checkValidity(validDate);
+                    criticalExtensions = new HashSet(criticalExtensions);
+
+                    // these extensions are handled by the algorithm
+                    criticalExtensions.remove(RFC3280CertPathUtilities.KEY_USAGE);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_MAPPINGS);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_CONSTRAINTS);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.BASIC_CONSTRAINTS);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME);
+                    criticalExtensions.remove(RFC3280CertPathUtilities.NAME_CONSTRAINTS);
                 }
-                catch (CertificateExpiredException e)
+                else
                 {
-                    throw new CertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
+                    criticalExtensions = new HashSet();
                 }
-                catch (CertificateNotYetValidException e)
-                {
-                    throw new CertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
-                }
-    
-                //
-                // (a) (3)
-                //
-                if (paramsPKIX.isRevocationEnabled())
-                {
-                    checkCRLs(paramsPKIX, cert, validDate, sign, workingPublicKey);
-                }
-    
-                //
-                // (a) (4) name chaining
-                //
-                if (!CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).equals(workingIssuerName))
-                {
-                    throw new CertPathValidatorException(
-                                "IssuerName(" + CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert) +
-                                ") does not match SubjectName(" + workingIssuerName +
-                                ") of signing certificate", null, certPath, index);
-                }
-    
-                //
-                // (b), (c) permitted and excluded subtree checking.
-                //
-                if (!(CertPathValidatorUtilities.isSelfIssued(cert) && (i < n)))
-                {
-                    X500Principal principal = CertPathValidatorUtilities.getSubjectPrincipal(cert);
-                    ASN1InputStream aIn = new ASN1InputStream(principal.getEncoded());
-                    ASN1Sequence    dns;
-    
-                    try
-                    {
-                        dns = (ASN1Sequence)aIn.readObject();
-                    }
-                    catch (IOException e)
-                    {
-                        throw new CertPathValidatorException("exception extracting subject name when checking subtrees");
-                    }
-    
-                    CertPathValidatorUtilities.checkPermittedDN(permittedSubtreesDN, dns);
-    
-                    CertPathValidatorUtilities.checkExcludedDN(excludedSubtreesDN, dns);
-            
-                    ASN1Sequence   altName = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, SUBJECT_ALTERNATIVE_NAME);
-                    if (altName != null)
-                    {
-                        for (int j = 0; j < altName.size(); j++)
-                        {
-                            ASN1TaggedObject o = (ASN1TaggedObject)altName.getObjectAt(j);
-    
-                            switch(o.getTagNo())
-                            {
-                            case 1:
-                                String email = DERIA5String.getInstance(o, true).getString();
-    
-                                CertPathValidatorUtilities.checkPermittedEmail(permittedSubtreesEmail, email);
-                                CertPathValidatorUtilities.checkExcludedEmail(excludedSubtreesEmail, email);
-                                break;
-                            case 4:
-                                ASN1Sequence altDN = ASN1Sequence.getInstance(o, true);
-    
-                                CertPathValidatorUtilities.checkPermittedDN(permittedSubtreesDN, altDN);
-                                CertPathValidatorUtilities.checkExcludedDN(excludedSubtreesDN, altDN);
-                                break;
-                            case 7:
-                                byte[] ip = ASN1OctetString.getInstance(o, true).getOctets();
-    
-                                CertPathValidatorUtilities.checkPermittedIP(permittedSubtreesIP, ip);
-                                CertPathValidatorUtilities.checkExcludedIP(excludedSubtreesIP, ip);
-                            }
-                        }
-                    }
-                }
-    
-                //
-                // (d) policy Information checking against initial policy and
-                // policy mapping
-                //
-                ASN1Sequence   certPolicies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, CERTIFICATE_POLICIES);
-                if (certPolicies != null && validPolicyTree != null)
-                {
-                    //
-                    // (d) (1)
-                    //
-                    Enumeration e = certPolicies.getObjects();
-                    Set         pols = new HashSet();
-                        
-                    while (e.hasMoreElements())
-                    {
-                        PolicyInformation   pInfo = PolicyInformation.getInstance(e.nextElement());
-                        DERObjectIdentifier pOid = pInfo.getPolicyIdentifier();
-                        
-                        pols.add(pOid.getId());
-    
-                        if (!ANY_POLICY.equals(pOid.getId()))
-                        {
-                            Set pq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
-                            
-                            boolean match = CertPathValidatorUtilities.processCertD1i(i, policyNodes, pOid, pq);
-                            
-                            if (!match)
-                            {
-                                CertPathValidatorUtilities.processCertD1ii(i, policyNodes, pOid, pq);
-                            }
-                        }
-                    }
-    
-                    if (acceptablePolicies == null || acceptablePolicies.contains(ANY_POLICY))
-                    {
-                        acceptablePolicies = pols;
-                    }
-                    else
-                    {
-                        Iterator    it = acceptablePolicies.iterator();
-                        Set         t1 = new HashSet();
-    
-                        while (it.hasNext())
-                        {
-                            Object  o = it.next();
-    
-                            if (pols.contains(o))
-                            {
-                                t1.add(o);
-                            }
-                        }
-    
-                        acceptablePolicies = t1;
-                    }
-    
-                    //
-                    // (d) (2)
-                    //
-                    if ((inhibitAnyPolicy > 0) || ((i < n) && CertPathValidatorUtilities.isSelfIssued(cert)))
-                    {
-                        e = certPolicies.getObjects();
-    
-                        while (e.hasMoreElements())
-                        {
-                            PolicyInformation   pInfo = PolicyInformation.getInstance(e.nextElement());
-    
-                            if (ANY_POLICY.equals(pInfo.getPolicyIdentifier().getId()))
-                            {
-                                Set    _apq   = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
-                                List      _nodes = policyNodes[i - 1];
-                                
-                                for (int k = 0; k < _nodes.size(); k++)
-                                {
-                                    PKIXPolicyNode _node = (PKIXPolicyNode)_nodes.get(k);
-                                    
-                                    Iterator _policySetIter = _node.getExpectedPolicies().iterator();
-                                    while (_policySetIter.hasNext())
-                                    {
-                                        Object _tmp = _policySetIter.next();
-                                        
-                                        String _policy;
-                                        if (_tmp instanceof String)
-                                        {
-                                            _policy = (String)_tmp;
-                                        }
-                                        else if (_tmp instanceof DERObjectIdentifier)
-                                        {
-                                            _policy = ((DERObjectIdentifier)_tmp).getId();
-                                        }
-                                        else
-                                        {
-                                            continue;
-                                        }
-                                        
-                                        boolean  _found        = false;
-                                        Iterator _childrenIter = _node.getChildren();
-    
-                                        while (_childrenIter.hasNext())
-                                        {
-                                            PKIXPolicyNode _child = (PKIXPolicyNode)_childrenIter.next();
-    
-                                            if (_policy.equals(_child.getValidPolicy()))
-                                            {
-                                                _found = true;
-                                            }
-                                        }
-    
-                                        if (!_found)
-                                        {
-                                            Set _newChildExpectedPolicies = new HashSet();
-                                            _newChildExpectedPolicies.add(_policy);
-    
-                                            PKIXPolicyNode _newChild = new PKIXPolicyNode(new ArrayList(),
-                                                                                          i,
-                                                                                          _newChildExpectedPolicies,
-                                                                                          _node,
-                                                                                          _apq,
-                                                                                          _policy,
-                                                                                          false);
-                                            _node.addChild(_newChild);
-                                            policyNodes[i].add(_newChild);
-                                        }
-                                    }
-                                }
-                                break;
-                            }
-                        }
-                    }
+
+                // (o)
+                RFC3280CertPathUtilities.prepareNextCertO(certPath, index, criticalExtensions, pathCheckers);
                 
-                    //
-                    // (d) (3)
-                    //
-                    for (int j = (i - 1); j >= 0; j--)
-                    {
-                        List      nodes = policyNodes[j];
-                        
-                        for (int k = 0; k < nodes.size(); k++)
-                        {
-                            PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
-                            if (!node.hasChildren())
-                            {
-                                validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node);
-                                if (validPolicyTree == null)
-                                {
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                
-                    //
-                    // d (4)
-                    //
-                    Set criticalExtensionOids = cert.getCriticalExtensionOIDs();
-                    
-                    if (criticalExtensionOids != null)
-                    {
-                        boolean critical = criticalExtensionOids.contains(CERTIFICATE_POLICIES);
-                    
-                        List      nodes = policyNodes[i];
-                        for (int j = 0; j < nodes.size(); j++)
-                        {
-                            PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(j);
-                            node.setCritical(critical);
-                        }
-                    }
-                }
-    
-                // 
-                // (e)
-                //
-                if (certPolicies == null)
-                {
-                    validPolicyTree = null;
-                }
-    
-                //
-                // (f)
-                //
-                if (explicitPolicy <= 0 && validPolicyTree == null)
-                {
-                    throw new CertPathValidatorException("No valid policy tree found when one expected.");
-                }
-    
-                //
-                // 6.1.4
-                //
-    
-                if (i != n) // if not at the end-entity certificate
-                {
-                    if (cert != null && cert.getVersion() == 1)
-                    {
-                        if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
-                        {
-                            throw new CertPathValidatorException(
-                                "Version 1 certs can't be used as intermediate certificates");
-                        }
-                    }
-    
-                    //
-                    //
-                    // (a) check the policy mappings
-                    //
-                    DERObject   pm = CertPathValidatorUtilities.getExtensionValue(cert, POLICY_MAPPINGS);
-                    if (pm != null)
-                    {
-                        ASN1Sequence mappings = (ASN1Sequence)pm;
-                    
-                        for (int j = 0; j < mappings.size(); j++)
-                        {
-                            ASN1Sequence    mapping = (ASN1Sequence)mappings.getObjectAt(j);
-    
-                            DERObjectIdentifier issuerDomainPolicy = (DERObjectIdentifier)mapping.getObjectAt(0);
-                            DERObjectIdentifier subjectDomainPolicy = (DERObjectIdentifier)mapping.getObjectAt(1);
-    
-                            if (ANY_POLICY.equals(issuerDomainPolicy.getId()))
-                            {
-                            
-                                throw new CertPathValidatorException("IssuerDomainPolicy is anyPolicy");
-                            }
-                        
-                            if (ANY_POLICY.equals(subjectDomainPolicy.getId()))
-                            {
-                            
-                                throw new CertPathValidatorException("SubjectDomainPolicy is anyPolicy");
-                            }
-                        }
-                    }
-                  
-                    // (b)
-                    //
-                    if (pm != null)
-                    {
-                        ASN1Sequence mappings = (ASN1Sequence)pm;
-                        Map m_idp = new HashMap();
-                        Set s_idp = new HashSet();
-                        
-                        for (int j = 0; j < mappings.size(); j++)
-                        {
-                            ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j);
-                            String id_p = ((DERObjectIdentifier)mapping.getObjectAt(0)).getId();
-                            String sd_p = ((DERObjectIdentifier)mapping.getObjectAt(1)).getId();
-                            Set tmp;
-                            
-                            if (!m_idp.containsKey(id_p))
-                            {
-                                tmp = new HashSet();
-                                tmp.add(sd_p);
-                                m_idp.put(id_p, tmp);
-                                s_idp.add(id_p);
-                            }
-                            else
-                            {
-                                tmp = (Set)m_idp.get(id_p);
-                                tmp.add(sd_p);
-                            }
-                        }
-    
-                        Iterator it_idp = s_idp.iterator();
-                        while (it_idp.hasNext())
-                        {
-                            String id_p = (String)it_idp.next();
-    
-                            //
-                            // (1)
-                            //
-                            if (policyMapping > 0)
-                            {
-                                boolean idp_found = false;
-                                Iterator nodes_i = policyNodes[i].iterator();
-                                while (nodes_i.hasNext())
-                                {
-                                    PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
-                                    if (node.getValidPolicy().equals(id_p))
-                                    {
-                                        idp_found = true;
-                                        node.expectedPolicies = (Set)m_idp.get(id_p);
-                                        break;
-                                    }
-                                }
-    
-                                if (!idp_found)
-                                {
-                                    nodes_i = policyNodes[i].iterator();
-                                    while (nodes_i.hasNext())
-                                    {
-                                        PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
-                                        if (ANY_POLICY.equals(node.getValidPolicy()))
-                                        {
-                                            Set pq = null;
-                                            ASN1Sequence policies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(
-                                                    cert, CERTIFICATE_POLICIES);
-                                            Enumeration e = policies.getObjects();
-                                            while (e.hasMoreElements())
-                                            {
-                                                PolicyInformation pinfo = PolicyInformation.getInstance(e.nextElement());
-                                                if (ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId()))
-                                                {
-                                                    pq = CertPathValidatorUtilities.getQualifierSet(pinfo.getPolicyQualifiers());
-                                                    break;
-                                                }
-                                            }
-                                            boolean ci = false;
-                                            if (cert.getCriticalExtensionOIDs() != null)
-                                            {
-                                                ci = cert.getCriticalExtensionOIDs().contains(CERTIFICATE_POLICIES);
-                                            }
-    
-                                            PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
-                                            if (ANY_POLICY.equals(p_node.getValidPolicy()))
-                                            {
-                                                PKIXPolicyNode c_node = new PKIXPolicyNode(
-                                                        new ArrayList(), i,
-                                                        (Set)m_idp.get(id_p),
-                                                        p_node, pq, id_p, ci);
-                                                p_node.addChild(c_node);
-                                                policyNodes[i].add(c_node);
-                                            }
-                                            break;
-                                        }
-                                    }
-                                }
-    
-                            //
-                            // (2)
-                            //
-                            }
-                            else if (policyMapping <= 0)
-                            {
-                                Iterator nodes_i = policyNodes[i].iterator();
-                                while (nodes_i.hasNext())
-                                {
-                                    PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
-                                    if (node.getValidPolicy().equals(id_p))
-                                    {
-                                        PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
-                                        p_node.removeChild(node);
-                                        nodes_i.remove();
-                                        for (int k = (i - 1); k >= 0; k--)
-                                        {
-                                            List nodes = policyNodes[k];
-                                            for (int l = 0; l < nodes.size(); l++)
-                                            {
-                                                PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l);
-                                                if (!node2.hasChildren())
-                                                {
-                                                    validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node2);
-                                                    if (validPolicyTree == null)
-                                                    {
-                                                        break;
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    
-                    //
-                    // (g) handle the name constraints extension
-                    //
-                    ASN1Sequence ncSeq = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, NAME_CONSTRAINTS);
-                    if (ncSeq != null)
-                    {
-                        NameConstraints nc = new NameConstraints(ncSeq);
-    
-                        //
-                        // (g) (1) permitted subtrees
-                        //
-                        ASN1Sequence permitted = nc.getPermittedSubtrees();
-                        if (permitted != null)
-                        {
-                            Enumeration e = permitted.getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                GeneralSubtree  subtree = GeneralSubtree.getInstance(e.nextElement());
-                                GeneralName     base = subtree.getBase();
-    
-                                switch(base.getTagNo())
-                                {
-                                    case 1:
-                                        permittedSubtreesEmail = CertPathValidatorUtilities.intersectEmail(permittedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString());
-                                        break;
-                                    case 4:
-                                        permittedSubtreesDN = CertPathValidatorUtilities.intersectDN(permittedSubtreesDN, (ASN1Sequence)base.getName());
-                                        break;
-                                    case 7:
-                                        permittedSubtreesIP = CertPathValidatorUtilities.intersectIP(permittedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets());
-                                        break;
-                                }
-                            }
-                        }
-                    
-                        //
-                        // (g) (2) excluded subtrees
-                        //
-                        ASN1Sequence excluded = nc.getExcludedSubtrees();
-                        if (excluded != null)
-                        {
-                            Enumeration e = excluded.getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                GeneralSubtree  subtree = GeneralSubtree.getInstance(e.nextElement());
-                                GeneralName     base = subtree.getBase();
-    
-                                switch(base.getTagNo())
-                                {
-                                case 1:
-                                    excludedSubtreesEmail = CertPathValidatorUtilities.unionEmail(excludedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString());
-                                    break;
-                                case 4:
-                                    excludedSubtreesDN = CertPathValidatorUtilities.unionDN(excludedSubtreesDN, (ASN1Sequence)base.getName());
-                                    break;
-                                case 7:
-                                    excludedSubtreesIP = CertPathValidatorUtilities.unionIP(excludedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets());
-                                    break;
-                                }
-                            }
-                        }
-                    }
-    
-                    //
-                    // (h)
-                    //
-                    if (!CertPathValidatorUtilities.isSelfIssued(cert))
-                    {
-                        //
-                        // (1)
-                        //
-                        if (explicitPolicy != 0)
-                        {
-                            explicitPolicy--;
-                        }
-                    
-                        //
-                        // (2)
-                        //
-                        if (policyMapping != 0)
-                        {
-                            policyMapping--;
-                        }
-                    
-                        //
-                        // (3)
-                        //
-                        if (inhibitAnyPolicy != 0)
-                        {
-                            inhibitAnyPolicy--;
-                        }
-                    }
-            
-                    //
-                    // (i)
-                    //
-                    ASN1Sequence pc = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, POLICY_CONSTRAINTS);
-                
-                    if (pc != null)
-                    {
-                        Enumeration policyConstraints = pc.getObjects();
-    
-                        while (policyConstraints.hasMoreElements())
-                        {
-                            ASN1TaggedObject    constraint = (ASN1TaggedObject)policyConstraints.nextElement();
-                            switch (constraint.getTagNo())
-                            {
-                            case 0:
-                                tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                                if (tmpInt < explicitPolicy)
-                                {
-                                    explicitPolicy = tmpInt;
-                                }
-                                break;
-                            case 1:
-                                tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                                if (tmpInt < policyMapping)
-                                {
-                                    policyMapping = tmpInt;
-                                }
-                            break;
-                            }
-                        }
-                    }
-            
-                    //
-                    // (j)
-                    //
-                    DERInteger iap = (DERInteger)CertPathValidatorUtilities.getExtensionValue(cert, INHIBIT_ANY_POLICY);
-                
-                    if (iap != null)
-                    {
-                        int _inhibitAnyPolicy = iap.getValue().intValue();
-                    
-                        if (_inhibitAnyPolicy < inhibitAnyPolicy)
-                        {
-                            inhibitAnyPolicy = _inhibitAnyPolicy;
-                        }
-                    }
-            
-                    //
-                    // (k)
-                    //
-                    BasicConstraints    bc = BasicConstraints.getInstance(
-                            CertPathValidatorUtilities.getExtensionValue(cert, BASIC_CONSTRAINTS));
-                    if (bc != null)
-                    {
-                        if (!(bc.isCA()))
-                        {
-                            throw new CertPathValidatorException("Not a CA certificate");
-                        }
-                    }
-                    else
-                    {
-                        if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
-                        {
-                            throw new CertPathValidatorException("Intermediate certificate lacks BasicConstraints");
-                        }
-                    }
-                
-                    //
-                    // (l)
-                    //
-                    if (!CertPathValidatorUtilities.isSelfIssued(cert))
-                    {
-                        if (maxPathLength <= 0)
-                        {
-                            throw new CertPathValidatorException("Max path length not greater than zero");
-                        }
-                    
-                        maxPathLength--;
-                    }
-            
-                    //
-                    // (m)
-                    //
-                    if (bc != null)
-                    {
-                        BigInteger          _pathLengthConstraint = bc.getPathLenConstraint();
-                
-                        if (_pathLengthConstraint != null)
-                        {
-                            int _plc = _pathLengthConstraint.intValue();
-    
-                            if (_plc < maxPathLength)
-                            {
-                                maxPathLength = _plc;
-                            }
-                        }
-                    }
-            
-                    //
-                    // (n)
-                    //
-                    boolean[] _usage = cert.getKeyUsage();
-                
-                    if ((_usage != null) && !_usage[5])
-                    {
-                        throw new CertPathValidatorException(
-                                    "Issuer certificate keyusage extension is critical an does not permit key signing.\n",
-                                    null, certPath, index);
-                    }
-    
-                    //
-                    // (o)
-                    //
-                    if (cert.getCriticalExtensionOIDs() != null)
-                    {
-                        Set criticalExtensions = new HashSet(cert.getCriticalExtensionOIDs());
-                        // these extensions are handle by the algorithem
-                        criticalExtensions.remove(KEY_USAGE);
-                        criticalExtensions.remove(CERTIFICATE_POLICIES);
-                        criticalExtensions.remove(POLICY_MAPPINGS);
-                        criticalExtensions.remove(INHIBIT_ANY_POLICY);
-                        criticalExtensions.remove(ISSUING_DISTRIBUTION_POINT);
-                        criticalExtensions.remove(DELTA_CRL_INDICATOR);
-                        criticalExtensions.remove(POLICY_CONSTRAINTS);
-                        criticalExtensions.remove(BASIC_CONSTRAINTS);
-                        criticalExtensions.remove(SUBJECT_ALTERNATIVE_NAME);
-                        criticalExtensions.remove(NAME_CONSTRAINTS);
-    
-                        tmpIter = pathCheckers.iterator();
-                        while (tmpIter.hasNext())
-                        {
-                            try
-                            {
-                                ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
-                            }
-                            catch (CertPathValidatorException e)
-                            {
-                                throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index);
-                            }
-                        }
-                        if (!criticalExtensions.isEmpty())
-                        {
-                            throw new CertPathValidatorException(
-                                "Certificate has unsupported critical extension", null, certPath, index);
-                        }
-                    }
-                }
-    
                 // set signing certificate for next round
                 sign = cert;
-                workingPublicKey = sign.getPublicKey();
+
+                // (c)
+                workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
+
+                // (d)
                 try
                 {
-                    workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
+                    workingPublicKey = CertPathValidatorUtilities.getNextWorkingKey(certPath.getCertificates(), index);
                 }
-                catch (IllegalArgumentException ex)
+                catch (CertPathValidatorException e)
                 {
-                    throw new CertPathValidatorException(sign.getSubjectDN().getName() + " :" + ex.toString());
+                    throw new CertPathValidatorException("Next working key could not be retrieved.", e, certPath, index);
                 }
+
                 workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey);
+                // (f)
                 workingPublicKeyAlgorithm = workingAlgId.getObjectId();
+                // (e)
                 workingPublicKeyParameters = workingAlgId.getParameters();
             }
-            catch (AnnotatedException e)
-            {
-                throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, index);
-            }
         }
 
         //
         // 6.1.5 Wrap-up procedure
         //
 
-        //
-        // (a)
-        //
-        if (!CertPathValidatorUtilities.isSelfIssued(cert) && (explicitPolicy != 0))
-        {
-            explicitPolicy--;
-        }
-    
-        //
-        // (b)
-        //
-        try
-        {
-            ASN1Sequence pc = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, POLICY_CONSTRAINTS);
-            if (pc != null)
-            {
-                Enumeration policyConstraints = pc.getObjects();
-    
-                while (policyConstraints.hasMoreElements())
-                {
-                    ASN1TaggedObject    constraint = (ASN1TaggedObject)policyConstraints.nextElement();
-                    switch (constraint.getTagNo())
-                    {
-                    case 0:
-                        tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                        if (tmpInt == 0)
-                        {
-                            explicitPolicy = 0;
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-        catch (AnnotatedException e)
-        {
-            throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, index);
-        }
-    
+        explicitPolicy = RFC3280CertPathUtilities.wrapupCertA(explicitPolicy, cert);
+
+        explicitPolicy = RFC3280CertPathUtilities.wrapupCertB(certPath, index + 1, explicitPolicy);
+
         //
         // (c) (d) and (e) are already done
         //
-    
+
         //
-        // (f) 
+        // (f)
         //
         Set criticalExtensions = cert.getCriticalExtensionOIDs();
-        
+
         if (criticalExtensions != null)
         {
             criticalExtensions = new HashSet(criticalExtensions);
-            // these extensions are handle by the algorithm
-            criticalExtensions.remove(KEY_USAGE);
-            criticalExtensions.remove(CERTIFICATE_POLICIES);
-            criticalExtensions.remove(POLICY_MAPPINGS);
-            criticalExtensions.remove(INHIBIT_ANY_POLICY);
-            criticalExtensions.remove(ISSUING_DISTRIBUTION_POINT);
-            criticalExtensions.remove(DELTA_CRL_INDICATOR);
-            criticalExtensions.remove(POLICY_CONSTRAINTS);
-            criticalExtensions.remove(BASIC_CONSTRAINTS);
-            criticalExtensions.remove(SUBJECT_ALTERNATIVE_NAME);
-            criticalExtensions.remove(NAME_CONSTRAINTS);
+            // these extensions are handled by the algorithm
+            criticalExtensions.remove(RFC3280CertPathUtilities.KEY_USAGE);
+            criticalExtensions.remove(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+            criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_MAPPINGS);
+            criticalExtensions.remove(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY);
+            criticalExtensions.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+            criticalExtensions.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+            criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_CONSTRAINTS);
+            criticalExtensions.remove(RFC3280CertPathUtilities.BASIC_CONSTRAINTS);
+            criticalExtensions.remove(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME);
+            criticalExtensions.remove(RFC3280CertPathUtilities.NAME_CONSTRAINTS);
+            criticalExtensions.remove(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS);
         }
         else
         {
             criticalExtensions = new HashSet();
         }
-        
-        tmpIter = pathCheckers.iterator();
-        while (tmpIter.hasNext())
-        {
-            try
-            {
-                ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
-            }
-            catch (CertPathValidatorException e)
-            {
-                throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index);
-            }
-        }
-        
-        if (!criticalExtensions.isEmpty())
-        {
-            throw new CertPathValidatorException(
-                "Certificate has unsupported critical extension", null, certPath, index);
-        }
 
-        //
-        // (g)
-        //
-        PKIXPolicyNode intersection;
-        
+        RFC3280CertPathUtilities.wrapupCertF(certPath, index + 1, pathCheckers, criticalExtensions);
 
-        //
-        // (g) (i)
-        //
-        if (validPolicyTree == null)
-        { 
-            if (paramsPKIX.isExplicitPolicyRequired())
-            {
-                throw new CertPathValidatorException("Explicit policy requested but none available.");
-            }
-            intersection = null;
-        }
-        else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) (ii)
-        {
-            if (paramsPKIX.isExplicitPolicyRequired())
-            {
-                if (acceptablePolicies.isEmpty())
-                {
-                    throw new CertPathValidatorException("Explicit policy requested but none available.");
-                }
-                else
-                {
-                    Set _validPolicyNodeSet = new HashSet();
-                    
-                    for (int j = 0; j < policyNodes.length; j++)
-                    {
-                        List      _nodeDepth = policyNodes[j];
-                        
-                        for (int k = 0; k < _nodeDepth.size(); k++)
-                        {
-                            PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
-                            
-                            if (ANY_POLICY.equals(_node.getValidPolicy()))
-                            {
-                                Iterator _iter = _node.getChildren();
-                                while (_iter.hasNext())
-                                {
-                                    _validPolicyNodeSet.add(_iter.next());
-                                }
-                            }
-                        }
-                    }
-                    
-                    Iterator _vpnsIter = _validPolicyNodeSet.iterator();
-                    while (_vpnsIter.hasNext())
-                    {
-                        PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
-                        String _validPolicy = _node.getValidPolicy();
-                        
-                        if (!acceptablePolicies.contains(_validPolicy))
-                        {
-                            //validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, _node);
-                        }
-                    }
-                    if (validPolicyTree != null)
-                    {
-                        for (int j = (n - 1); j >= 0; j--)
-                        {
-                            List      nodes = policyNodes[j];
-                            
-                            for (int k = 0; k < nodes.size(); k++)
-                            {
-                                PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
-                                if (!node.hasChildren())
-                                {
-                                    validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
+        PKIXPolicyNode intersection = RFC3280CertPathUtilities.wrapupCertG(certPath, paramsPKIX, userInitialPolicySet,
+                index + 1, policyNodes, validPolicyTree, acceptablePolicies);
 
-            intersection = validPolicyTree;
-        }
-        else
-        {
-            //
-            // (g) (iii)
-            //
-            // This implementation is not exactly same as the one described in RFC3280.
-            // However, as far as the validation result is concerned, both produce 
-            // adequate result. The only difference is whether AnyPolicy is remain 
-            // in the policy tree or not. 
-            //
-            // (g) (iii) 1
-            //
-            Set _validPolicyNodeSet = new HashSet();
-            
-            for (int j = 0; j < policyNodes.length; j++)
-            {
-                List      _nodeDepth = policyNodes[j];
-                
-                for (int k = 0; k < _nodeDepth.size(); k++)
-                {
-                    PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
-                    
-                    if (ANY_POLICY.equals(_node.getValidPolicy()))
-                    {
-                        Iterator _iter = _node.getChildren();
-                        while (_iter.hasNext())
-                        {
-                            PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next();
-                            if (!ANY_POLICY.equals(_c_node.getValidPolicy()))
-                            {
-                                _validPolicyNodeSet.add(_c_node);
-                            }
-                        }
-                    }
-                }
-            }
-            
-            //
-            // (g) (iii) 2
-            //
-            Iterator _vpnsIter = _validPolicyNodeSet.iterator();
-            while (_vpnsIter.hasNext())
-            {
-                PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
-                String _validPolicy = _node.getValidPolicy();
-
-                if (!userInitialPolicySet.contains(_validPolicy))
-                {
-                    validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, _node);
-                }
-            }
-            
-            //
-            // (g) (iii) 4
-            //
-            if (validPolicyTree != null)
-            {
-                for (int j = (n - 1); j >= 0; j--)
-                {
-                    List      nodes = policyNodes[j];
-                    
-                    for (int k = 0; k < nodes.size(); k++)
-                    {
-                        PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
-                        if (!node.hasChildren())
-                        {
-                            validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node);
-                        }
-                    }
-                }
-            }
-            
-            intersection = validPolicyTree;
-        }
- 
         if ((explicitPolicy > 0) || (intersection != null))
         {
-            return new PKIXCertPathValidatorResult(trust, intersection, workingPublicKey);
+            return new PKIXCertPathValidatorResult(trust, intersection, cert.getPublicKey());
         }
 
         throw new CertPathValidatorException("Path processing failed on policy.", null, certPath, index);
     }
-    
-    private void checkCRLs(PKIXParameters paramsPKIX, X509Certificate cert, Date validDate, X509Certificate sign, PublicKey workingPublicKey) 
-        throws AnnotatedException
-    {
-        X509CRLSelector crlselect;
-        crlselect = new X509CRLSelector();
-    
-        try
-        {
-            crlselect.addIssuerName(CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).getEncoded());
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException("Cannot extract issuer from certificate: " + e, e);
-        }
-    
-        crlselect.setCertificateChecking(cert);
-    
-        Iterator crl_iter = CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getCertStores()).iterator();
-        boolean validCrlFound = false;
-        X509CRLEntry crl_entry;
-        while (crl_iter.hasNext())
-        {
-            X509CRL crl = (X509CRL)crl_iter.next();
-    
-            if (cert.getNotAfter().after(crl.getThisUpdate()))
-            {
-                if (crl.getNextUpdate() == null
-                    || validDate.before(crl.getNextUpdate())) 
-                {
-                    validCrlFound = true;
-                }
-    
-                if (sign != null)
-                {
-                    boolean[] keyusage = sign.getKeyUsage();
-    
-                    if (keyusage != null
-                        && (keyusage.length < 7 || !keyusage[CRL_SIGN]))
-                    {
-                        throw new AnnotatedException(
-                            "Issuer certificate keyusage extension does not permit crl signing.\n" + sign);
-                    }
-                }
-    
-                try
-                {
-                    crl.verify(workingPublicKey, "BC");
-                }
-                catch (Exception e)
-                {
-                    throw new AnnotatedException("can't verify CRL: " + e, e);
-                }
-    
-                crl_entry = crl.getRevokedCertificate(cert.getSerialNumber());
-                if (crl_entry != null
-                    && !validDate.before(crl_entry.getRevocationDate()))
-                {
-                    String reason = null;
-                    
-                    if (crl_entry.hasExtensions())
-                    {
-                        DEREnumerated reasonCode = DEREnumerated.getInstance(CertPathValidatorUtilities.getExtensionValue(crl_entry, X509Extensions.ReasonCode.getId()));
-                        if (reasonCode != null)
-                        {
-                            reason = crlReasons[reasonCode.getValue().intValue()];
-                        }
-                    }
-                    
-                    String message = "Certificate revocation after " + crl_entry.getRevocationDate();
-                    
-                    if (reason != null)
-                    {
-                        message += ", reason: " + reason;
-                    }
-                    
-                    throw new AnnotatedException(message);
-                }
-    
-                //
-                // check the DeltaCRL indicator, base point and the issuing distribution point
-                //
-                DERObject idp = CertPathValidatorUtilities.getExtensionValue(crl, ISSUING_DISTRIBUTION_POINT);
-                DERObject dci = CertPathValidatorUtilities.getExtensionValue(crl, DELTA_CRL_INDICATOR);
-    
-                if (dci != null)
-                {
-                    X509CRLSelector baseSelect = new X509CRLSelector();
-    
-                    try
-                    {
-                        baseSelect.addIssuerName(CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded());
-                    }
-                    catch (IOException e)
-                    {
-                        throw new AnnotatedException("can't extract issuer from certificate: " + e, e);
-                    }
-    
-                    baseSelect.setMinCRLNumber(((DERInteger)dci).getPositiveValue());
-                    baseSelect.setMaxCRLNumber(((DERInteger)CertPathValidatorUtilities.getExtensionValue(crl, CRL_NUMBER)).getPositiveValue().subtract(BigInteger.valueOf(1)));
-                    
-                    boolean  foundBase = false;
-                    Iterator it  = CertPathValidatorUtilities.findCRLs(baseSelect, paramsPKIX.getCertStores()).iterator();
-                    while (it.hasNext())
-                    {
-                        X509CRL base = (X509CRL)it.next();
-    
-                        DERObject baseIdp = CertPathValidatorUtilities.getExtensionValue(base, ISSUING_DISTRIBUTION_POINT);
-                        
-                        if (idp == null)
-                        {
-                            if (baseIdp == null)
-                            {
-                                foundBase = true;
-                                break;
-                            }
-                        }
-                        else
-                        {
-                            if (idp.equals(baseIdp))
-                            {
-                                foundBase = true;
-                                break;
-                            }
-                        }
-                    }
-                    
-                    if (!foundBase)
-                    {
-                        throw new AnnotatedException("No base CRL for delta CRL");
-                    }
-                }
-    
-                if (idp != null)
-                {
-                    IssuingDistributionPoint    p = IssuingDistributionPoint.getInstance(idp);
-                    BasicConstraints    bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert, BASIC_CONSTRAINTS));
-                    
-                    if (p.onlyContainsUserCerts() && (bc != null && bc.isCA()))
-                    {
-                        throw new AnnotatedException("CA Cert CRL only contains user certificates");
-                    }
-                    
-                    if (p.onlyContainsCACerts() && (bc == null || !bc.isCA()))
-                    {
-                        throw new AnnotatedException("End CRL only contains CA certificates");
-                    }
-                    
-                    if (p.onlyContainsAttributeCerts())
-                    {
-                        throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted");
-                    }
-                }
-            }
-        }
-    
-        if (!validCrlFound)
-        {
-            throw new AnnotatedException("no valid CRL found");
-        }
-    }
+
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java b/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java
new file mode 100644
index 0000000..0af311f
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java
@@ -0,0 +1,1924 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralSubtree;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class PKIXNameConstraintValidator
+{
+    private Set excludedSubtreesDN = new HashSet();
+
+    private Set excludedSubtreesDNS = new HashSet();
+
+    private Set excludedSubtreesEmail = new HashSet();
+
+    private Set excludedSubtreesURI = new HashSet();
+
+    private Set excludedSubtreesIP = new HashSet();
+
+    private Set permittedSubtreesDN;
+
+    private Set permittedSubtreesDNS;
+
+    private Set permittedSubtreesEmail;
+
+    private Set permittedSubtreesURI;
+
+    private Set permittedSubtreesIP;
+
+    public PKIXNameConstraintValidator()
+    {
+    }
+
+    private static boolean withinDNSubtree(
+        ASN1Sequence dns,
+        ASN1Sequence subtree)
+    {
+        if (subtree.size() < 1)
+        {
+            return false;
+        }
+
+        if (subtree.size() > dns.size())
+        {
+            return false;
+        }
+
+        for (int j = subtree.size() - 1; j >= 0; j--)
+        {
+            if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j)))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void checkPermittedDN(ASN1Sequence dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        checkPermittedDN(permittedSubtreesDN, dns);
+    }
+
+    public void checkExcludedDN(ASN1Sequence dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        checkExcludedDN(excludedSubtreesDN, dns);
+    }
+
+    private void checkPermittedDN(Set permitted, ASN1Sequence dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        if (permitted.isEmpty() && dns.size() == 0)
+        {
+            return;
+        }
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+            if (withinDNSubtree(dns, subtree))
+            {
+                return;
+            }
+        }
+
+        throw new PKIXNameConstraintValidatorException(
+            "Subject distinguished name is not from a permitted subtree");
+    }
+
+    private void checkExcludedDN(Set excluded, ASN1Sequence dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+            if (withinDNSubtree(dns, subtree))
+            {
+                throw new PKIXNameConstraintValidatorException(
+                    "Subject distinguished name is from an excluded subtree");
+            }
+        }
+    }
+
+    private Set intersectDN(Set permitted, Set dns)
+    {
+        Set intersect = new HashSet();
+        for (Iterator it = dns.iterator(); it.hasNext();)
+        {
+            ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it
+                .next()).getBase().getName().getDERObject());
+            if (permitted == null)
+            {
+                if (dn != null)
+                {
+                    intersect.add(dn);
+                }
+            }
+            else
+            {
+                Iterator _iter = permitted.iterator();
+                while (_iter.hasNext())
+                {
+                    ASN1Sequence subtree = (ASN1Sequence)_iter.next();
+
+                    if (withinDNSubtree(dn, subtree))
+                    {
+                        intersect.add(dn);
+                    }
+                    else if (withinDNSubtree(subtree, dn))
+                    {
+                        intersect.add(subtree);
+                    }
+                }
+            }
+        }
+        return intersect;
+    }
+
+    private Set unionDN(Set excluded, ASN1Sequence dn)
+    {
+        if (excluded.isEmpty())
+        {
+            if (dn == null)
+            {
+                return excluded;
+            }
+            excluded.add(dn);
+
+            return excluded;
+        }
+        else
+        {
+            Set intersect = new HashSet();
+
+            Iterator it = excluded.iterator();
+            while (it.hasNext())
+            {
+                ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+                if (withinDNSubtree(dn, subtree))
+                {
+                    intersect.add(subtree);
+                }
+                else if (withinDNSubtree(subtree, dn))
+                {
+                    intersect.add(dn);
+                }
+                else
+                {
+                    intersect.add(subtree);
+                    intersect.add(dn);
+                }
+            }
+
+            return intersect;
+        }
+    }
+
+    private Set intersectEmail(Set permitted, Set emails)
+    {
+        Set intersect = new HashSet();
+        for (Iterator it = emails.iterator(); it.hasNext();)
+        {
+            String email = extractNameAsString(((GeneralSubtree)it.next())
+                .getBase());
+
+            if (permitted == null)
+            {
+                if (email != null)
+                {
+                    intersect.add(email);
+                }
+            }
+            else
+            {
+                Iterator it2 = permitted.iterator();
+                while (it2.hasNext())
+                {
+                    String _permitted = (String)it2.next();
+
+                    intersectEmail(email, _permitted, intersect);
+                }
+            }
+        }
+        return intersect;
+    }
+
+    private Set unionEmail(Set excluded, String email)
+    {
+        if (excluded.isEmpty())
+        {
+            if (email == null)
+            {
+                return excluded;
+            }
+            excluded.add(email);
+            return excluded;
+        }
+        else
+        {
+            Set union = new HashSet();
+
+            Iterator it = excluded.iterator();
+            while (it.hasNext())
+            {
+                String _excluded = (String)it.next();
+
+                unionEmail(_excluded, email, union);
+            }
+
+            return union;
+        }
+    }
+
+    /**
+     * Returns the intersection of the permitted IP ranges in
+     * <code>permitted</code> with <code>ip</code>.
+     *
+     * @param permitted A <code>Set</code> of permitted IP addresses with
+     *                  their subnet mask as byte arrays.
+     * @param ips       The IP address with its subnet mask.
+     * @return The <code>Set</code> of permitted IP ranges intersected with
+     *         <code>ip</code>.
+     */
+    private Set intersectIP(Set permitted, Set ips)
+    {
+        Set intersect = new HashSet();
+        for (Iterator it = ips.iterator(); it.hasNext();)
+        {
+            byte[] ip = ASN1OctetString.getInstance(
+                ((GeneralSubtree)it.next()).getBase().getName()).getOctets();
+            if (permitted == null)
+            {
+                if (ip != null)
+                {
+                    intersect.add(ip);
+                }
+            }
+            else
+            {
+                Iterator it2 = permitted.iterator();
+                while (it2.hasNext())
+                {
+                    byte[] _permitted = (byte[])it2.next();
+                    intersect.addAll(intersectIPRange(_permitted, ip));
+                }
+            }
+        }
+        return intersect;
+    }
+
+    /**
+     * Returns the union of the excluded IP ranges in <code>excluded</code>
+     * with <code>ip</code>.
+     *
+     * @param excluded A <code>Set</code> of excluded IP addresses with their
+     *                 subnet mask as byte arrays.
+     * @param ip       The IP address with its subnet mask.
+     * @return The <code>Set</code> of excluded IP ranges unified with
+     *         <code>ip</code> as byte arrays.
+     */
+    private Set unionIP(Set excluded, byte[] ip)
+    {
+        if (excluded.isEmpty())
+        {
+            if (ip == null)
+            {
+                return excluded;
+            }
+            excluded.add(ip);
+
+            return excluded;
+        }
+        else
+        {
+            Set union = new HashSet();
+
+            Iterator it = excluded.iterator();
+            while (it.hasNext())
+            {
+                byte[] _excluded = (byte[])it.next();
+                union.addAll(unionIPRange(_excluded, ip));
+            }
+
+            return union;
+        }
+    }
+
+    /**
+     * Calculates the union if two IP ranges.
+     *
+     * @param ipWithSubmask1 The first IP address with its subnet mask.
+     * @param ipWithSubmask2 The second IP address with its subnet mask.
+     * @return A <code>Set</code> with the union of both addresses.
+     */
+    private Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+    {
+        Set set = new HashSet();
+
+        // difficult, adding always all IPs is not wrong
+        if (Arrays.areEqual(ipWithSubmask1, ipWithSubmask2))
+        {
+            set.add(ipWithSubmask1);
+        }
+        else
+        {
+            set.add(ipWithSubmask1);
+            set.add(ipWithSubmask2);
+        }
+        return set;
+    }
+
+    /**
+     * Calculates the interesction if two IP ranges.
+     *
+     * @param ipWithSubmask1 The first IP address with its subnet mask.
+     * @param ipWithSubmask2 The second IP address with its subnet mask.
+     * @return A <code>Set</code> with the single IP address with its subnet
+     *         mask as a byte array or an empty <code>Set</code>.
+     */
+    private Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+    {
+        if (ipWithSubmask1.length != ipWithSubmask2.length)
+        {
+            return Collections.EMPTY_SET;
+        }
+        byte[][] temp = extractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
+        byte ip1[] = temp[0];
+        byte subnetmask1[] = temp[1];
+        byte ip2[] = temp[2];
+        byte subnetmask2[] = temp[3];
+
+        byte minMax[][] = minMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
+        byte[] min;
+        byte[] max;
+        max = min(minMax[1], minMax[3]);
+        min = max(minMax[0], minMax[2]);
+
+        // minimum IP address must be bigger than max
+        if (compareTo(min, max) == 1)
+        {
+            return Collections.EMPTY_SET;
+        }
+        // OR keeps all significant bits
+        byte[] ip = or(minMax[0], minMax[2]);
+        byte[] subnetmask = or(subnetmask1, subnetmask2);
+        return Collections.singleton(ipWithSubnetMask(ip, subnetmask));
+    }
+
+    /**
+     * Concatenates the IP address with its subnet mask.
+     *
+     * @param ip         The IP address.
+     * @param subnetMask Its subnet mask.
+     * @return The concatenated IP address with its subnet mask.
+     */
+    private byte[] ipWithSubnetMask(byte[] ip, byte[] subnetMask)
+    {
+        int ipLength = ip.length;
+        byte[] temp = new byte[ipLength * 2];
+        System.arraycopy(ip, 0, temp, 0, ipLength);
+        System.arraycopy(subnetMask, 0, temp, ipLength, ipLength);
+        return temp;
+    }
+
+    /**
+     * Splits the IP addresses and their subnet mask.
+     *
+     * @param ipWithSubmask1 The first IP address with the subnet mask.
+     * @param ipWithSubmask2 The second IP address with the subnet mask.
+     * @return An array with two elements. Each element contains the IP address
+     *         and the subnet mask in this order.
+     */
+    private byte[][] extractIPsAndSubnetMasks(
+        byte[] ipWithSubmask1,
+        byte[] ipWithSubmask2)
+    {
+        int ipLength = ipWithSubmask1.length / 2;
+        byte ip1[] = new byte[ipLength];
+        byte subnetmask1[] = new byte[ipLength];
+        System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength);
+        System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
+
+        byte ip2[] = new byte[ipLength];
+        byte subnetmask2[] = new byte[ipLength];
+        System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength);
+        System.arraycopy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
+        return new byte[][]
+            {ip1, subnetmask1, ip2, subnetmask2};
+    }
+
+    /**
+     * Based on the two IP addresses and their subnet masks the IP range is
+     * computed for each IP address - subnet mask pair and returned as the
+     * minimum IP address and the maximum address of the range.
+     *
+     * @param ip1         The first IP address.
+     * @param subnetmask1 The subnet mask of the first IP address.
+     * @param ip2         The second IP address.
+     * @param subnetmask2 The subnet mask of the second IP address.
+     * @return A array with two elements. The first/second element contains the
+     *         min and max IP address of the first/second IP address and its
+     *         subnet mask.
+     */
+    private byte[][] minMaxIPs(
+        byte[] ip1,
+        byte[] subnetmask1,
+        byte[] ip2,
+        byte[] subnetmask2)
+    {
+        int ipLength = ip1.length;
+        byte[] min1 = new byte[ipLength];
+        byte[] max1 = new byte[ipLength];
+
+        byte[] min2 = new byte[ipLength];
+        byte[] max2 = new byte[ipLength];
+
+        for (int i = 0; i < ipLength; i++)
+        {
+            min1[i] = (byte)(ip1[i] & subnetmask1[i]);
+            max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
+
+            min2[i] = (byte)(ip2[i] & subnetmask2[i]);
+            max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
+        }
+
+        return new byte[][]{min1, max1, min2, max2};
+    }
+
+    private void checkPermittedEmail(Set permitted, String email)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            String str = ((String)it.next());
+
+            if (emailIsConstrained(email, str))
+            {
+                return;
+            }
+        }
+
+        if (email.length() == 0 && permitted.size() == 0)
+        {
+            return;
+        }
+
+        throw new PKIXNameConstraintValidatorException(
+            "Subject email address is not from a permitted subtree.");
+    }
+
+    private void checkExcludedEmail(Set excluded, String email)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            String str = (String)it.next();
+
+            if (emailIsConstrained(email, str))
+            {
+                throw new PKIXNameConstraintValidatorException(
+                    "Email address is from an excluded subtree.");
+            }
+        }
+    }
+
+    /**
+     * Checks if the IP <code>ip</code> is included in the permitted set
+     * <code>permitted</code>.
+     *
+     * @param permitted A <code>Set</code> of permitted IP addresses with
+     *                  their subnet mask as byte arrays.
+     * @param ip        The IP address.
+     * @throws PKIXNameConstraintValidatorException
+     *          if the IP is not permitted.
+     */
+    private void checkPermittedIP(Set permitted, byte[] ip)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            byte[] ipWithSubnet = (byte[])it.next();
+
+            if (isIPConstrained(ip, ipWithSubnet))
+            {
+                return;
+            }
+        }
+        if (ip.length == 0 && permitted.size() == 0)
+        {
+            return;
+        }
+        throw new PKIXNameConstraintValidatorException(
+            "IP is not from a permitted subtree.");
+    }
+
+    /**
+     * Checks if the IP <code>ip</code> is included in the excluded set
+     * <code>excluded</code>.
+     *
+     * @param excluded A <code>Set</code> of excluded IP addresses with their
+     *                 subnet mask as byte arrays.
+     * @param ip       The IP address.
+     * @throws PKIXNameConstraintValidatorException
+     *          if the IP is excluded.
+     */
+    private void checkExcludedIP(Set excluded, byte[] ip)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            byte[] ipWithSubnet = (byte[])it.next();
+
+            if (isIPConstrained(ip, ipWithSubnet))
+            {
+                throw new PKIXNameConstraintValidatorException(
+                    "IP is from an excluded subtree.");
+            }
+        }
+    }
+
+    /**
+     * Checks if the IP address <code>ip</code> is constrained by
+     * <code>constraint</code>.
+     *
+     * @param ip         The IP address.
+     * @param constraint The constraint. This is an IP address concatenated with
+     *                   its subnetmask.
+     * @return <code>true</code> if constrained, <code>false</code>
+     *         otherwise.
+     */
+    private boolean isIPConstrained(byte ip[], byte[] constraint)
+    {
+        int ipLength = ip.length;
+
+        if (ipLength != (constraint.length / 2))
+        {
+            return false;
+        }
+
+        byte[] subnetMask = new byte[ipLength];
+        System.arraycopy(constraint, ipLength, subnetMask, 0, ipLength);
+
+        byte[] permittedSubnetAddress = new byte[ipLength];
+
+        byte[] ipSubnetAddress = new byte[ipLength];
+
+        // the resulting IP address by applying the subnet mask
+        for (int i = 0; i < ipLength; i++)
+        {
+            permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
+            ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
+        }
+
+        return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
+    }
+
+    private boolean emailIsConstrained(String email, String constraint)
+    {
+        String sub = email.substring(email.indexOf('@') + 1);
+        // a particular mailbox
+        if (constraint.indexOf('@') != -1)
+        {
+            if (email.equalsIgnoreCase(constraint))
+            {
+                return true;
+            }
+        }
+        // on particular host
+        else if (!(constraint.charAt(0) == '.'))
+        {
+            if (sub.equalsIgnoreCase(constraint))
+            {
+                return true;
+            }
+        }
+        // address in sub domain
+        else if (withinDomain(sub, constraint))
+        {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean withinDomain(String testDomain, String domain)
+    {
+        String tempDomain = domain;
+        if (tempDomain.startsWith("."))
+        {
+            tempDomain = tempDomain.substring(1);
+        }
+        String[] domainParts = Strings.split(tempDomain, '.');
+        String[] testDomainParts = Strings.split(testDomain, '.');
+        // must have at least one subdomain
+        if (testDomainParts.length <= domainParts.length)
+        {
+            return false;
+        }
+        int d = testDomainParts.length - domainParts.length;
+        for (int i = -1; i < domainParts.length; i++)
+        {
+            if (i == -1)
+            {
+                if (testDomainParts[i + d].equals(""))
+                {
+                    return false;
+                }
+            }
+            else if (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d]))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void checkPermittedDNS(Set permitted, String dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            String str = ((String)it.next());
+
+            // is sub domain
+            if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+            {
+                return;
+            }
+        }
+        if (dns.length() == 0 && permitted.size() == 0)
+        {
+            return;
+        }
+        throw new PKIXNameConstraintValidatorException(
+            "DNS is not from a permitted subtree.");
+    }
+
+    private void checkExcludedDNS(Set excluded, String dns)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            String str = ((String)it.next());
+
+            // is sub domain or the same
+            if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+            {
+                throw new PKIXNameConstraintValidatorException(
+                    "DNS is from an excluded subtree.");
+            }
+        }
+    }
+
+    /**
+     * The common part of <code>email1</code> and <code>email2</code> is
+     * added to the union <code>union</code>. If <code>email1</code> and
+     * <code>email2</code> have nothing in common they are added both.
+     *
+     * @param email1 Email address constraint 1.
+     * @param email2 Email address constraint 2.
+     * @param union  The union.
+     */
+    private void unionEmail(String email1, String email2, Set union)
+    {
+        // email1 is a particular address
+        if (email1.indexOf('@') != -1)
+        {
+            String _sub = email1.substring(email1.indexOf('@') + 1);
+            // both are a particular mailbox
+            if (email2.indexOf('@') != -1)
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(_sub, email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (_sub.equalsIgnoreCase(email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+        // email1 specifies a domain
+        else if (email1.startsWith("."))
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (withinDomain(_sub, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2)
+                    || email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email2);
+                }
+                else if (withinDomain(email2, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            else
+            {
+                if (withinDomain(email2, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+        // email specifies a host
+        else
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (_sub.equalsIgnoreCase(email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+    }
+
+    private void unionURI(String email1, String email2, Set union)
+    {
+        // email1 is a particular address
+        if (email1.indexOf('@') != -1)
+        {
+            String _sub = email1.substring(email1.indexOf('@') + 1);
+            // both are a particular mailbox
+            if (email2.indexOf('@') != -1)
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(_sub, email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (_sub.equalsIgnoreCase(email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+        // email1 specifies a domain
+        else if (email1.startsWith("."))
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (withinDomain(_sub, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2)
+                    || email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email2);
+                }
+                else if (withinDomain(email2, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            else
+            {
+                if (withinDomain(email2, email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+        // email specifies a host
+        else
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (_sub.equalsIgnoreCase(email1))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2))
+                {
+                    union.add(email2);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    union.add(email1);
+                }
+                else
+                {
+                    union.add(email1);
+                    union.add(email2);
+                }
+            }
+        }
+    }
+
+    private Set intersectDNS(Set permitted, Set dnss)
+    {
+        Set intersect = new HashSet();
+        for (Iterator it = dnss.iterator(); it.hasNext();)
+        {
+            String dns = extractNameAsString(((GeneralSubtree)it.next())
+                .getBase());
+            if (permitted == null)
+            {
+                if (dns != null)
+                {
+                    intersect.add(dns);
+                }
+            }
+            else
+            {
+                Iterator _iter = permitted.iterator();
+                while (_iter.hasNext())
+                {
+                    String _permitted = (String)_iter.next();
+
+                    if (withinDomain(_permitted, dns))
+                    {
+                        intersect.add(_permitted);
+                    }
+                    else if (withinDomain(dns, _permitted))
+                    {
+                        intersect.add(dns);
+                    }
+                }
+            }
+        }
+
+        return intersect;
+    }
+
+    protected Set unionDNS(Set excluded, String dns)
+    {
+        if (excluded.isEmpty())
+        {
+            if (dns == null)
+            {
+                return excluded;
+            }
+            excluded.add(dns);
+
+            return excluded;
+        }
+        else
+        {
+            Set union = new HashSet();
+
+            Iterator _iter = excluded.iterator();
+            while (_iter.hasNext())
+            {
+                String _permitted = (String)_iter.next();
+
+                if (withinDomain(_permitted, dns))
+                {
+                    union.add(dns);
+                }
+                else if (withinDomain(dns, _permitted))
+                {
+                    union.add(_permitted);
+                }
+                else
+                {
+                    union.add(_permitted);
+                    union.add(dns);
+                }
+            }
+
+            return union;
+        }
+    }
+
+    /**
+     * The most restricting part from <code>email1</code> and
+     * <code>email2</code> is added to the intersection <code>intersect</code>.
+     *
+     * @param email1    Email address constraint 1.
+     * @param email2    Email address constraint 2.
+     * @param intersect The intersection.
+     */
+    private void intersectEmail(String email1, String email2, Set intersect)
+    {
+        // email1 is a particular address
+        if (email1.indexOf('@') != -1)
+        {
+            String _sub = email1.substring(email1.indexOf('@') + 1);
+            // both are a particular mailbox
+            if (email2.indexOf('@') != -1)
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(_sub, email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (_sub.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+        }
+        // email specifies a domain
+        else if (email1.startsWith("."))
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (withinDomain(_sub, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2)
+                    || email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+                else if (withinDomain(email2, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            else
+            {
+                if (withinDomain(email2, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+        }
+        // email1 specifies a host
+        else
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email2.indexOf('@') + 1);
+                if (_sub.equalsIgnoreCase(email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+        }
+    }
+
+    private void checkExcludedURI(Set excluded, String uri)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (excluded.isEmpty())
+        {
+            return;
+        }
+
+        Iterator it = excluded.iterator();
+
+        while (it.hasNext())
+        {
+            String str = ((String)it.next());
+
+            if (isUriConstrained(uri, str))
+            {
+                throw new PKIXNameConstraintValidatorException(
+                    "URI is from an excluded subtree.");
+            }
+        }
+    }
+
+    private Set intersectURI(Set permitted, Set uris)
+    {
+        Set intersect = new HashSet();
+        for (Iterator it = uris.iterator(); it.hasNext();)
+        {
+            String uri = extractNameAsString(((GeneralSubtree)it.next())
+                .getBase());
+            if (permitted == null)
+            {
+                if (uri != null)
+                {
+                    intersect.add(uri);
+                }
+            }
+            else
+            {
+                Iterator _iter = permitted.iterator();
+                while (_iter.hasNext())
+                {
+                    String _permitted = (String)_iter.next();
+                    intersectURI(_permitted, uri, intersect);
+                }
+            }
+        }
+        return intersect;
+    }
+
+    private Set unionURI(Set excluded, String uri)
+    {
+        if (excluded.isEmpty())
+        {
+            if (uri == null)
+            {
+                return excluded;
+            }
+            excluded.add(uri);
+
+            return excluded;
+        }
+        else
+        {
+            Set union = new HashSet();
+
+            Iterator _iter = excluded.iterator();
+            while (_iter.hasNext())
+            {
+                String _excluded = (String)_iter.next();
+
+                unionURI(_excluded, uri, union);
+            }
+
+            return union;
+        }
+    }
+
+    private void intersectURI(String email1, String email2, Set intersect)
+    {
+        // email1 is a particular address
+        if (email1.indexOf('@') != -1)
+        {
+            String _sub = email1.substring(email1.indexOf('@') + 1);
+            // both are a particular mailbox
+            if (email2.indexOf('@') != -1)
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(_sub, email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (_sub.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+        }
+        // email specifies a domain
+        else if (email1.startsWith("."))
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email1.indexOf('@') + 1);
+                if (withinDomain(_sub, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2)
+                    || email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+                else if (withinDomain(email2, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            else
+            {
+                if (withinDomain(email2, email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+        }
+        // email1 specifies a host
+        else
+        {
+            if (email2.indexOf('@') != -1)
+            {
+                String _sub = email2.substring(email2.indexOf('@') + 1);
+                if (_sub.equalsIgnoreCase(email1))
+                {
+                    intersect.add(email2);
+                }
+            }
+            // email2 specifies a domain
+            else if (email2.startsWith("."))
+            {
+                if (withinDomain(email1, email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+            // email2 specifies a particular host
+            else
+            {
+                if (email1.equalsIgnoreCase(email2))
+                {
+                    intersect.add(email1);
+                }
+            }
+        }
+    }
+
+    private void checkPermittedURI(Set permitted, String uri)
+        throws PKIXNameConstraintValidatorException
+    {
+        if (permitted == null)
+        {
+            return;
+        }
+
+        Iterator it = permitted.iterator();
+
+        while (it.hasNext())
+        {
+            String str = ((String)it.next());
+
+            if (isUriConstrained(uri, str))
+            {
+                return;
+            }
+        }
+        if (uri.length() == 0 && permitted.size() == 0)
+        {
+            return;
+        }
+        throw new PKIXNameConstraintValidatorException(
+            "URI is not from a permitted subtree.");
+    }
+
+    private boolean isUriConstrained(String uri, String constraint)
+    {
+        String host = extractHostFromURL(uri);
+        // a host
+        if (!constraint.startsWith("."))
+        {
+            if (host.equalsIgnoreCase(constraint))
+            {
+                return true;
+            }
+        }
+
+        // in sub domain or domain
+        else if (withinDomain(host, constraint))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static String extractHostFromURL(String url)
+    {
+        // see RFC 1738
+        // remove ':' after protocol, e.g. http:
+        String sub = url.substring(url.indexOf(':') + 1);
+        // extract host from Common Internet Scheme Syntax, e.g. http://
+        if (sub.indexOf("//") != -1)
+        {
+            sub = sub.substring(sub.indexOf("//") + 2);
+        }
+        // first remove port, e.g. http://test.com:21
+        if (sub.lastIndexOf(':') != -1)
+        {
+            sub = sub.substring(0, sub.lastIndexOf(':'));
+        }
+        // remove user and password, e.g. http://john:password@test.com
+        sub = sub.substring(sub.indexOf(':') + 1);
+        sub = sub.substring(sub.indexOf('@') + 1);
+        // remove local parts, e.g. http://test.com/bla
+        if (sub.indexOf('/') != -1)
+        {
+            sub = sub.substring(0, sub.indexOf('/'));
+        }
+        return sub;
+    }
+
+    /**
+     * Checks if the given GeneralName is in the permitted set.
+     *
+     * @param name The GeneralName
+     * @throws PKIXNameConstraintValidatorException
+     *          If the <code>name</code>
+     */
+    public void checkPermitted(GeneralName name)
+        throws PKIXNameConstraintValidatorException
+    {
+        switch (name.getTagNo())
+        {
+            case 1:
+                checkPermittedEmail(permittedSubtreesEmail,
+                    extractNameAsString(name));
+                break;
+            case 2:
+                checkPermittedDNS(permittedSubtreesDNS, DERIA5String.getInstance(
+                    name.getName()).getString());
+                break;
+            case 4:
+                checkPermittedDN(ASN1Sequence.getInstance(name.getName()
+                    .getDERObject()));
+                break;
+            case 6:
+                checkPermittedURI(permittedSubtreesURI, DERIA5String.getInstance(
+                    name.getName()).getString());
+                break;
+            case 7:
+                byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+                checkPermittedIP(permittedSubtreesIP, ip);
+        }
+    }
+
+    /**
+     * Check if the given GeneralName is contained in the excluded set.
+     *
+     * @param name The GeneralName.
+     * @throws PKIXNameConstraintValidatorException
+     *          If the <code>name</code> is
+     *          excluded.
+     */
+    public void checkExcluded(GeneralName name)
+        throws PKIXNameConstraintValidatorException
+    {
+        switch (name.getTagNo())
+        {
+            case 1:
+                checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
+                break;
+            case 2:
+                checkExcludedDNS(excludedSubtreesDNS, DERIA5String.getInstance(
+                    name.getName()).getString());
+                break;
+            case 4:
+                checkExcludedDN(ASN1Sequence.getInstance(name.getName()
+                    .getDERObject()));
+                break;
+            case 6:
+                checkExcludedURI(excludedSubtreesURI, DERIA5String.getInstance(
+                    name.getName()).getString());
+                break;
+            case 7:
+                byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+                checkExcludedIP(excludedSubtreesIP, ip);
+        }
+    }
+
+    /**
+     * Updates the permitted set of these name constraints with the intersection
+     * with the given subtree.
+     *
+     * @param permitted The permitted subtrees
+     */
+
+    public void intersectPermittedSubtree(ASN1Sequence permitted)
+    {
+        Map subtreesMap = new HashMap();
+
+        // group in sets in a map ordered by tag no.
+        for (Enumeration e = permitted.getObjects(); e.hasMoreElements();)
+        {
+            GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement());
+            // BEGIN android-changed
+            Integer tagNo = Integer.valueOf(subtree.getBase().getTagNo());
+            // END android-changed
+            if (subtreesMap.get(tagNo) == null)
+            {
+                subtreesMap.put(tagNo, new HashSet());
+            }
+            ((Set)subtreesMap.get(tagNo)).add(subtree);
+        }
+
+        for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext();)
+        {
+            Map.Entry entry = (Map.Entry)it.next();
+
+            // go through all subtree groups
+            switch (((Integer)entry.getKey()).intValue())
+            {
+                case 1:
+                    permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
+                        (Set)entry.getValue());
+                    break;
+                case 2:
+                    permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
+                        (Set)entry.getValue());
+                    break;
+                case 4:
+                    permittedSubtreesDN = intersectDN(permittedSubtreesDN,
+                        (Set)entry.getValue());
+                    break;
+                case 6:
+                    permittedSubtreesURI = intersectURI(permittedSubtreesURI,
+                        (Set)entry.getValue());
+                    break;
+                case 7:
+                    permittedSubtreesIP = intersectIP(permittedSubtreesIP,
+                        (Set)entry.getValue());
+            }
+        }
+    }
+
+    private String extractNameAsString(GeneralName name)
+    {
+        return DERIA5String.getInstance(name.getName()).getString();
+    }
+
+    public void intersectEmptyPermittedSubtree(int nameType)
+    {
+        switch (nameType)
+        {
+        case 1:
+            permittedSubtreesEmail = new HashSet();
+            break;
+        case 2:
+            permittedSubtreesDNS = new HashSet();
+            break;
+        case 4:
+            permittedSubtreesDN = new HashSet();
+            break;
+        case 6:
+            permittedSubtreesURI = new HashSet();
+            break;
+        case 7:
+            permittedSubtreesIP = new HashSet();
+        }
+    }
+
+    /**
+     * Adds a subtree to the excluded set of these name constraints.
+     *
+     * @param subtree A subtree with an excluded GeneralName.
+     */
+    public void addExcludedSubtree(GeneralSubtree subtree)
+    {
+        GeneralName base = subtree.getBase();
+
+        switch (base.getTagNo())
+        {
+            case 1:
+                excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
+                    extractNameAsString(base));
+                break;
+            case 2:
+                excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
+                    extractNameAsString(base));
+                break;
+            case 4:
+                excludedSubtreesDN = unionDN(excludedSubtreesDN,
+                    (ASN1Sequence)base.getName().getDERObject());
+                break;
+            case 6:
+                excludedSubtreesURI = unionURI(excludedSubtreesURI,
+                    extractNameAsString(base));
+                break;
+            case 7:
+                excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString
+                    .getInstance(base.getName()).getOctets());
+                break;
+        }
+    }
+
+    /**
+     * Returns the maximum IP address.
+     *
+     * @param ip1 The first IP address.
+     * @param ip2 The second IP address.
+     * @return The maximum IP address.
+     */
+    private static byte[] max(byte[] ip1, byte[] ip2)
+    {
+        for (int i = 0; i < ip1.length; i++)
+        {
+            if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
+            {
+                return ip1;
+            }
+        }
+        return ip2;
+    }
+
+    /**
+     * Returns the minimum IP address.
+     *
+     * @param ip1 The first IP address.
+     * @param ip2 The second IP address.
+     * @return The minimum IP address.
+     */
+    private static byte[] min(byte[] ip1, byte[] ip2)
+    {
+        for (int i = 0; i < ip1.length; i++)
+        {
+            if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
+            {
+                return ip1;
+            }
+        }
+        return ip2;
+    }
+
+    /**
+     * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1
+     * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
+     * otherwise.
+     *
+     * @param ip1 The first IP address.
+     * @param ip2 The second IP address.
+     * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
+     */
+    private static int compareTo(byte[] ip1, byte[] ip2)
+    {
+        if (Arrays.areEqual(ip1, ip2))
+        {
+            return 0;
+        }
+        if (Arrays.areEqual(max(ip1, ip2), ip1))
+        {
+            return 1;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the logical OR of the IP addresses <code>ip1</code> and
+     * <code>ip2</code>.
+     *
+     * @param ip1 The first IP address.
+     * @param ip2 The second IP address.
+     * @return The OR of <code>ip1</code> and <code>ip2</code>.
+     */
+    private static byte[] or(byte[] ip1, byte[] ip2)
+    {
+        byte[] temp = new byte[ip1.length];
+        for (int i = 0; i < ip1.length; i++)
+        {
+            temp[i] = (byte)(ip1[i] | ip2[i]);
+        }
+        return temp;
+    }
+
+    public int hashCode()
+    {
+        return hashCollection(excludedSubtreesDN)
+            + hashCollection(excludedSubtreesDNS)
+            + hashCollection(excludedSubtreesEmail)
+            + hashCollection(excludedSubtreesIP)
+            + hashCollection(excludedSubtreesURI)
+            + hashCollection(permittedSubtreesDN)
+            + hashCollection(permittedSubtreesDNS)
+            + hashCollection(permittedSubtreesEmail)
+            + hashCollection(permittedSubtreesIP)
+            + hashCollection(permittedSubtreesURI);
+    }
+
+    private int hashCollection(Collection coll)
+    {
+        if (coll == null)
+        {
+            return 0;
+        }
+        int hash = 0;
+        Iterator it1 = coll.iterator();
+        while (it1.hasNext())
+        {
+            Object o = it1.next();
+            if (o instanceof byte[])
+            {
+                hash += Arrays.hashCode((byte[])o);
+            }
+            else
+            {
+                hash += o.hashCode();
+            }
+        }
+        return hash;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (!(o instanceof PKIXNameConstraintValidator))
+        {
+            return false;
+        }
+        PKIXNameConstraintValidator constraintValidator = (PKIXNameConstraintValidator)o;
+        return collectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
+            && collectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
+            && collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
+            && collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
+            && collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
+            && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
+    }
+
+    private boolean collectionsAreEqual(Collection coll1, Collection coll2)
+    {
+        if (coll1 == coll2)
+        {
+            return true;
+        }
+        if (coll1 == null || coll2 == null)
+        {
+            return false;
+        }
+        if (coll1.size() != coll2.size())
+        {
+            return false;
+        }
+        Iterator it1 = coll1.iterator();
+
+        while (it1.hasNext())
+        {
+            Object a = it1.next();
+            Iterator it2 = coll2.iterator();
+            boolean found = false;
+            while (it2.hasNext())
+            {
+                Object b = it2.next();
+                if (equals(a, b))
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean equals(Object o1, Object o2)
+    {
+        if (o1 == o2)
+        {
+            return true;
+        }
+        if (o1 == null || o2 == null)
+        {
+            return false;
+        }
+        if (o1 instanceof byte[] && o2 instanceof byte[])
+        {
+            return Arrays.areEqual((byte[])o1, (byte[])o2);
+        }
+        else
+        {
+            return o1.equals(o2);
+        }
+    }
+
+    /**
+     * Stringifies an IPv4 or v6 address with subnet mask.
+     *
+     * @param ip The IP with subnet mask.
+     * @return The stringified IP address.
+     */
+    private String stringifyIP(byte[] ip)
+    {
+        String temp = "";
+        for (int i = 0; i < ip.length / 2; i++)
+        {
+            temp += Integer.toString(ip[i] & 0x00FF) + ".";
+        }
+        temp = temp.substring(0, temp.length() - 1);
+        temp += "/";
+        for (int i = ip.length / 2; i < ip.length; i++)
+        {
+            temp += Integer.toString(ip[i] & 0x00FF) + ".";
+        }
+        temp = temp.substring(0, temp.length() - 1);
+        return temp;
+    }
+
+    private String stringifyIPCollection(Set ips)
+    {
+        String temp = "";
+        temp += "[";
+        for (Iterator it = ips.iterator(); it.hasNext();)
+        {
+            temp += stringifyIP((byte[])it.next()) + ",";
+        }
+        if (temp.length() > 1)
+        {
+            temp = temp.substring(0, temp.length() - 1);
+        }
+        temp += "]";
+        return temp;
+    }
+
+    public String toString()
+    {
+        String temp = "";
+        temp += "permitted:\n";
+        if (permittedSubtreesDN != null)
+        {
+            temp += "DN:\n";
+            temp += permittedSubtreesDN.toString() + "\n";
+        }
+        if (permittedSubtreesDNS != null)
+        {
+            temp += "DNS:\n";
+            temp += permittedSubtreesDNS.toString() + "\n";
+        }
+        if (permittedSubtreesEmail != null)
+        {
+            temp += "Email:\n";
+            temp += permittedSubtreesEmail.toString() + "\n";
+        }
+        if (permittedSubtreesURI != null)
+        {
+            temp += "URI:\n";
+            temp += permittedSubtreesURI.toString() + "\n";
+        }
+        if (permittedSubtreesIP != null)
+        {
+            temp += "IP:\n";
+            temp += stringifyIPCollection(permittedSubtreesIP) + "\n";
+        }
+        temp += "excluded:\n";
+        if (!excludedSubtreesDN.isEmpty())
+        {
+            temp += "DN:\n";
+            temp += excludedSubtreesDN.toString() + "\n";
+        }
+        if (!excludedSubtreesDNS.isEmpty())
+        {
+            temp += "DNS:\n";
+            temp += excludedSubtreesDNS.toString() + "\n";
+        }
+        if (!excludedSubtreesEmail.isEmpty())
+        {
+            temp += "Email:\n";
+            temp += excludedSubtreesEmail.toString() + "\n";
+        }
+        if (!excludedSubtreesURI.isEmpty())
+        {
+            temp += "URI:\n";
+            temp += excludedSubtreesURI.toString() + "\n";
+        }
+        if (!excludedSubtreesIP.isEmpty())
+        {
+            temp += "IP:\n";
+            temp += stringifyIPCollection(excludedSubtreesIP) + "\n";
+        }
+        return temp;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java b/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java
new file mode 100644
index 0000000..b06d5e5
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.jce.provider;
+
+public class PKIXNameConstraintValidatorException
+    extends Exception
+{
+    public PKIXNameConstraintValidatorException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/ProviderUtil.java b/src/main/java/org/bouncycastle/jce/provider/ProviderUtil.java
new file mode 100644
index 0000000..b4f700d
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/ProviderUtil.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.jce.ProviderConfigurationPermission;
+// BEGIN android-removed
+// import org.bouncycastle.jce.provider.asymmetric.ec.EC5Util;
+// END android-removed
+import org.bouncycastle.jce.interfaces.ConfigurableProvider;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.ECParameterSpec;
+// END android-removed
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Permission;
+
+public class ProviderUtil
+{
+    private static final long  MAX_MEMORY = Runtime.getRuntime().maxMemory();
+
+    private static Permission BC_EC_LOCAL_PERMISSION = new ProviderConfigurationPermission(
+                                                   "BC", ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA);
+    private static Permission BC_EC_PERMISSION = new ProviderConfigurationPermission(
+                                                   "BC", ConfigurableProvider.EC_IMPLICITLY_CA);
+
+    private static ThreadLocal threadSpec = new ThreadLocal();
+    // BEGIN android-removed
+    // private static volatile ECParameterSpec ecImplicitCaParams;
+    // END android-removed
+
+    static void setParameter(String parameterName, Object parameter)
+    {
+        SecurityManager securityManager = System.getSecurityManager();
+
+        // BEGIN android-removed
+        // if (parameterName.equals(ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA))
+        // {
+        //     ECParameterSpec curveSpec;
+        //
+        //     if (securityManager != null)
+        //     {
+        //         securityManager.checkPermission(BC_EC_LOCAL_PERMISSION);
+        //     }
+        //
+        //     if (parameter instanceof ECParameterSpec || parameter == null)
+        //     {
+        //         curveSpec = (ECParameterSpec)parameter;
+        //     }
+        //     else  // assume java.security.spec
+        //     {
+        //         curveSpec = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+        //     }
+        //
+        //     if (curveSpec == null)
+        //     {
+        //         threadSpec.remove();
+        //     }
+        //     else
+        //     {
+        //         threadSpec.set(curveSpec);
+        //     }
+        // }
+        // else if (parameterName.equals(ConfigurableProvider.EC_IMPLICITLY_CA))
+        // {
+        //     if (securityManager != null)
+        //     {
+        //         securityManager.checkPermission(BC_EC_PERMISSION);
+        //     }
+        //
+        //     if (parameter instanceof ECParameterSpec || parameter == null)
+        //     {
+        //         ecImplicitCaParams = (ECParameterSpec)parameter;
+        //     }
+        //     else  // assume java.security.spec
+        //     {
+        //         ecImplicitCaParams = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+        //     }
+        // }
+        // END android-removed
+    }
+
+    // BEGIN android-removed
+    // public static ECParameterSpec getEcImplicitlyCa()
+    // {
+    //     ECParameterSpec spec = (ECParameterSpec)threadSpec.get();
+    //
+    //     if (spec != null)
+    //     {
+    //         return spec;
+    //     }
+    //
+    //     return ecImplicitCaParams;
+    // }
+    // END android-removed
+
+    static int getReadLimit(InputStream in)
+        throws IOException
+    {
+        if (in instanceof ByteArrayInputStream)
+        {
+            return in.available();
+        }
+
+        if (MAX_MEMORY > Integer.MAX_VALUE)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return (int)MAX_MEMORY;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
new file mode 100644
index 0000000..921ed3b
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -0,0 +1,2609 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509Extension;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.GeneralSubtree;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.NameConstraints;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+import org.bouncycastle.x509.X509CRLStoreSelector;
+import org.bouncycastle.x509.X509CertStoreSelector;
+
+public class RFC3280CertPathUtilities
+{
+
+    /**
+     * If the complete CRL includes an issuing distribution point (IDP) CRL
+     * extension check the following:
+     * <p/>
+     * (i) If the distribution point name is present in the IDP CRL extension
+     * and the distribution field is present in the DP, then verify that one of
+     * the names in the IDP matches one of the names in the DP. If the
+     * distribution point name is present in the IDP CRL extension and the
+     * distribution field is omitted from the DP, then verify that one of the
+     * names in the IDP matches one of the names in the cRLIssuer field of the
+     * DP.
+     * </p>
+     * <p/>
+     * (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL
+     * extension, verify that the certificate does not include the basic
+     * constraints extension with the cA boolean asserted.
+     * </p>
+     * <p/>
+     * (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL
+     * extension, verify that the certificate includes the basic constraints
+     * extension with the cA boolean asserted.
+     * </p>
+     * <p/>
+     * (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted.
+     * </p>
+     *
+     * @param dp   The distribution point.
+     * @param cert The certificate.
+     * @param crl  The CRL.
+     * @throws AnnotatedException if one of the conditions is not met or an error occurs.
+     */
+    protected static void processCRLB2(
+        DistributionPoint dp,
+        Object cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        IssuingDistributionPoint idp = null;
+        try
+        {
+            idp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+                RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+        }
+        // (b) (2) (i)
+        // distribution point name is present
+        if (idp != null)
+        {
+            if (idp.getDistributionPoint() != null)
+            {
+                // make list of names
+                DistributionPointName dpName = IssuingDistributionPoint.getInstance(idp).getDistributionPoint();
+                List names = new ArrayList();
+
+                if (dpName.getType() == DistributionPointName.FULL_NAME)
+                {
+                    GeneralName[] genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+                    for (int j = 0; j < genNames.length; j++)
+                    {
+                        names.add(genNames[j]);
+                    }
+                }
+                if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+                {
+                    ASN1EncodableVector vec = new ASN1EncodableVector();
+                    try
+                    {
+                        Enumeration e = ASN1Sequence.getInstance(
+                            ASN1Sequence.fromByteArray(CertPathValidatorUtilities.getIssuerPrincipal(crl)
+                                .getEncoded())).getObjects();
+                        while (e.hasMoreElements())
+                        {
+                            vec.add((DEREncodable)e.nextElement());
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException("Could not read CRL issuer.", e);
+                    }
+                    vec.add(dpName.getName());
+                    names.add(new GeneralName(X509Name.getInstance(new DERSequence(vec))));
+                }
+                boolean matches = false;
+                // verify that one of the names in the IDP matches one
+                // of the names in the DP.
+                if (dp.getDistributionPoint() != null)
+                {
+                    dpName = dp.getDistributionPoint();
+                    GeneralName[] genNames = null;
+                    if (dpName.getType() == DistributionPointName.FULL_NAME)
+                    {
+                        genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+                    }
+                    if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+                    {
+                        if (dp.getCRLIssuer() != null)
+                        {
+                            genNames = dp.getCRLIssuer().getNames();
+                        }
+                        else
+                        {
+                            genNames = new GeneralName[1];
+                            try
+                            {
+                                genNames[0] = new GeneralName(new X509Name(
+                                    (ASN1Sequence)ASN1Sequence.fromByteArray(CertPathValidatorUtilities
+                                        .getEncodedIssuerPrincipal(cert).getEncoded())));
+                            }
+                            catch (IOException e)
+                            {
+                                throw new AnnotatedException("Could not read certificate issuer.", e);
+                            }
+                        }
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            Enumeration e = ASN1Sequence.getInstance(genNames[j].getName().getDERObject()).getObjects();
+                            ASN1EncodableVector vec = new ASN1EncodableVector();
+                            while (e.hasMoreElements())
+                            {
+                                vec.add((DEREncodable)e.nextElement());
+                            }
+                            vec.add(dpName.getName());
+                            genNames[j] = new GeneralName(new X509Name(new DERSequence(vec)));
+                        }
+                    }
+                    if (genNames != null)
+                    {
+                        for (int j = 0; j < genNames.length; j++)
+                        {
+                            if (names.contains(genNames[j]))
+                            {
+                                matches = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!matches)
+                    {
+                        throw new AnnotatedException(
+                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+                    }
+                }
+                // verify that one of the names in
+                // the IDP matches one of the names in the cRLIssuer field of
+                // the DP
+                else
+                {
+                    if (dp.getCRLIssuer() == null)
+                    {
+                        throw new AnnotatedException("Either the cRLIssuer or the distributionPoint field must "
+                            + "be contained in DistributionPoint.");
+                    }
+                    GeneralName[] genNames = dp.getCRLIssuer().getNames();
+                    for (int j = 0; j < genNames.length; j++)
+                    {
+                        if (names.contains(genNames[j]))
+                        {
+                            matches = true;
+                            break;
+                        }
+                    }
+                    if (!matches)
+                    {
+                        throw new AnnotatedException(
+                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+                    }
+                }
+            }
+            BasicConstraints bc = null;
+            try
+            {
+                bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue((X509Extension)cert,
+                    BASIC_CONSTRAINTS));
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException("Basic constraints extension could not be decoded.", e);
+            }
+
+            if (cert instanceof X509Certificate)
+            {
+                // (b) (2) (ii)
+                if (idp.onlyContainsUserCerts() && (bc != null && bc.isCA()))
+                {
+                    throw new AnnotatedException("CA Cert CRL only contains user certificates.");
+                }
+
+                // (b) (2) (iii)
+                if (idp.onlyContainsCACerts() && (bc == null || !bc.isCA()))
+                {
+                    throw new AnnotatedException("End CRL only contains CA certificates.");
+                }
+            }
+
+            // (b) (2) (iv)
+            if (idp.onlyContainsAttributeCerts())
+            {
+                throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted.");
+            }
+        }
+    }
+
+    /**
+     * If the DP includes cRLIssuer, then verify that the issuer field in the
+     * complete CRL matches cRLIssuer in the DP and that the complete CRL
+     * contains an issuing distribution point extension with the indirectCRL
+     * boolean asserted. Otherwise, verify that the CRL issuer matches the
+     * certificate issuer.
+     *
+     * @param dp   The distribution point.
+     * @param cert The certificate ot attribute certificate.
+     * @param crl  The CRL for <code>cert</code>.
+     * @throws AnnotatedException if one of the above conditions does not apply or an error
+     *                            occurs.
+     */
+    protected static void processCRLB1(
+        DistributionPoint dp,
+        Object cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        DERObject idp = CertPathValidatorUtilities.getExtensionValue(crl, ISSUING_DISTRIBUTION_POINT);
+        boolean isIndirect = false;
+        if (idp != null)
+        {
+            if (IssuingDistributionPoint.getInstance(idp).isIndirectCRL())
+            {
+                isIndirect = true;
+            }
+        }
+        byte[] issuerBytes = CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded();
+
+        boolean matchIssuer = false;
+        if (dp.getCRLIssuer() != null)
+        {
+            GeneralName genNames[] = dp.getCRLIssuer().getNames();
+            for (int j = 0; j < genNames.length; j++)
+            {
+                if (genNames[j].getTagNo() == GeneralName.directoryName)
+                {
+                    try
+                    {
+                        if (Arrays.areEqual(genNames[j].getName().getDERObject().getEncoded(), issuerBytes))
+                        {
+                            matchIssuer = true;
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new AnnotatedException(
+                            "CRL issuer information from distribution point cannot be decoded.", e);
+                    }
+                }
+            }
+            if (matchIssuer && !isIndirect)
+            {
+                throw new AnnotatedException("Distribution point contains cRLIssuer field but CRL is not indirect.");
+            }
+            if (!matchIssuer)
+            {
+                throw new AnnotatedException("CRL issuer of CRL does not match CRL issuer of distribution point.");
+            }
+        }
+        else
+        {
+            if (CertPathValidatorUtilities.getIssuerPrincipal(crl).equals(
+                CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert)))
+            {
+                matchIssuer = true;
+            }
+        }
+        if (!matchIssuer)
+        {
+            throw new AnnotatedException("Cannot find matching CRL issuer for certificate.");
+        }
+    }
+
+    protected static ReasonsMask processCRLD(
+        X509CRL crl,
+        DistributionPoint dp)
+        throws AnnotatedException
+    {
+        IssuingDistributionPoint idp = null;
+        try
+        {
+            idp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+                RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+        }
+        // (d) (1)
+        if (idp != null && idp.getOnlySomeReasons() != null && dp.getReasons() != null)
+        {
+            return new ReasonsMask(dp.getReasons().intValue()).intersect(new ReasonsMask(idp.getOnlySomeReasons()
+                .intValue()));
+        }
+        // (d) (4)
+        if ((idp == null || idp.getOnlySomeReasons() == null) && dp.getReasons() == null)
+        {
+            return ReasonsMask.allReasons;
+        }
+        // (d) (2) and (d)(3)
+        return (dp.getReasons() == null
+            ? ReasonsMask.allReasons
+            : new ReasonsMask(dp.getReasons().intValue())).intersect(idp == null
+            ? ReasonsMask.allReasons
+            : new ReasonsMask(idp.getOnlySomeReasons().intValue()));
+
+    }
+
+    protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId();
+
+    protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId();
+
+    protected static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId();
+
+    protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId();
+
+    protected static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId();
+
+    protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId();
+
+    protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId();
+
+    protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId();
+
+    protected static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId();
+
+    protected static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId();
+
+    protected static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId();
+
+    protected static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId();
+
+    protected static final String KEY_USAGE = X509Extensions.KeyUsage.getId();
+
+    protected static final String CRL_NUMBER = X509Extensions.CRLNumber.getId();
+
+    protected static final String ANY_POLICY = "2.5.29.32.0";
+
+    /*
+     * key usage bits
+     */
+    protected static final int KEY_CERT_SIGN = 5;
+
+    protected static final int CRL_SIGN = 6;
+
+    /**
+     * Obtain and validate the certification path for the complete CRL issuer.
+     * If a key usage extension is present in the CRL issuer's certificate,
+     * verify that the cRLSign bit is set.
+     *
+     * @param crl                CRL which contains revocation information for the certificate
+     *                           <code>cert</code>.
+     * @param cert               The attribute certificate or certificate to check if it is
+     *                           revoked.
+     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+     * @param defaultCRLSignKey  The public key of the issuer certificate
+     *                           <code>defaultCRLSignCert</code>.
+     * @param paramsPKIX         paramsPKIX PKIX parameters.
+     * @param certPathCerts      The certificates on the certification path.
+     * @return A <code>Set</code> with all keys of possible CRL issuer
+     *         certificates.
+     * @throws AnnotatedException if the CRL is not valid or the status cannot be checked or
+     *                            some error occurs.
+     */
+    protected static Set processCRLF(
+        X509CRL crl,
+        Object cert,
+        X509Certificate defaultCRLSignCert,
+        PublicKey defaultCRLSignKey,
+        ExtendedPKIXParameters paramsPKIX,
+        List certPathCerts)
+        throws AnnotatedException
+    {
+        // (f)
+
+        // get issuer from CRL
+        X509CertStoreSelector selector = new X509CertStoreSelector();
+        try
+        {
+            byte[] issuerPrincipal = CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded();
+            selector.setSubject(issuerPrincipal);
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException(
+                "Subject criteria for certificate selector to find issuer certificate for CRL could not be set.", e);
+        }
+
+        // get CRL signing certs
+        Collection coll;
+        try
+        {
+            coll = CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getStores());
+            coll.addAll(CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getAdditionalStores()));
+            coll.addAll(CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getCertStores()));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Issuer certificate for CRL cannot be searched.", e);
+        }
+
+        coll.add(defaultCRLSignCert);
+
+        Iterator cert_it = coll.iterator();
+
+        List validCerts = new ArrayList();
+        List validKeys = new ArrayList();
+
+        while (cert_it.hasNext())
+        {
+            X509Certificate signingCert = (X509Certificate)cert_it.next();
+
+            /*
+             * CA of the certificate, for which this CRL is checked, has also
+             * signed CRL, so skip the path validation, because is already done
+             */
+            if (signingCert.equals(defaultCRLSignCert))
+            {
+                validCerts.add(signingCert);
+                validKeys.add(defaultCRLSignKey);
+                continue;
+            }
+            try
+            {
+                CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
+                selector = new X509CertStoreSelector();
+                selector.setCertificate(signingCert);
+                ExtendedPKIXParameters temp = (ExtendedPKIXParameters)paramsPKIX.clone();
+                temp.setTargetCertConstraints(selector);
+                ExtendedPKIXBuilderParameters params = (ExtendedPKIXBuilderParameters)ExtendedPKIXBuilderParameters
+                    .getInstance(temp);
+                /*
+                 * if signingCert is placed not higher on the cert path a
+                 * dependency loop results. CRL for cert is checked, but
+                 * signingCert is needed for checking the CRL which is dependent
+                 * on checking cert because it is higher in the cert path and so
+                 * signing signingCert transitively. so, revocation is disabled,
+                 * forgery attacks of the CRL are detected in this outer loop
+                 * for all other it must be enabled to prevent forgery attacks
+                 */
+                if (certPathCerts.contains(signingCert))
+                {
+                    params.setRevocationEnabled(false);
+                }
+                else
+                {
+                    params.setRevocationEnabled(true);
+                }
+                List certs = builder.build(params).getCertPath().getCertificates();
+                validCerts.add(signingCert);
+                validKeys.add(CertPathValidatorUtilities.getNextWorkingKey(certs, 0));
+            }
+            catch (CertPathBuilderException e)
+            {
+                throw new AnnotatedException("Internal error.", e);
+            }
+            catch (CertPathValidatorException e)
+            {
+                throw new AnnotatedException("Public key of issuer certificate of CRL could not be retrieved.", e);
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+
+        Set checkKeys = new HashSet();
+
+        AnnotatedException lastException = null;
+        for (int i = 0; i < validCerts.size(); i++)
+        {
+            X509Certificate signCert = (X509Certificate)validCerts.get(i);
+            boolean[] keyusage = signCert.getKeyUsage();
+
+            if (keyusage != null && (keyusage.length < 7 || !keyusage[CRL_SIGN]))
+            {
+                lastException = new AnnotatedException(
+                    "Issuer certificate key usage extension does not permit CRL signing.");
+            }
+            else
+            {
+                checkKeys.add(validKeys.get(i));
+            }
+        }
+
+        if (checkKeys.isEmpty() && lastException == null)
+        {
+            throw new AnnotatedException("Cannot find a valid issuer certificate.");
+        }
+        if (checkKeys.isEmpty() && lastException != null)
+        {
+            throw lastException;
+        }
+
+        return checkKeys;
+    }
+
+    protected static PublicKey processCRLG(
+        X509CRL crl,
+        Set keys)
+        throws AnnotatedException
+    {
+        Exception lastException = null;
+        for (Iterator it = keys.iterator(); it.hasNext();)
+        {
+            PublicKey key = (PublicKey)it.next();
+            try
+            {
+                crl.verify(key);
+                return key;
+            }
+            catch (Exception e)
+            {
+                lastException = e;
+            }
+        }
+        throw new AnnotatedException("Cannot verify CRL.", lastException);
+    }
+
+    protected static X509CRL processCRLH(
+        Set deltacrls,
+        PublicKey key)
+        throws AnnotatedException
+    {
+        Exception lastException = null;
+
+        for (Iterator it = deltacrls.iterator(); it.hasNext();)
+        {
+            X509CRL crl = (X509CRL)it.next();
+            try
+            {
+                crl.verify(key);
+                return crl;
+            }
+            catch (Exception e)
+            {
+                lastException = e;
+            }
+        }
+
+        if (lastException != null)
+        {
+            throw new AnnotatedException("Cannot verify delta CRL.", lastException);
+        }
+        return null;
+    }
+
+    protected static Set processCRLA1i(
+        Date currentDate,
+        ExtendedPKIXParameters paramsPKIX,
+        X509Certificate cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        Set set = new HashSet();
+        if (paramsPKIX.isUseDeltasEnabled())
+        {
+            CRLDistPoint freshestCRL = null;
+            try
+            {
+                freshestCRL = CRLDistPoint
+                    .getInstance(CertPathValidatorUtilities.getExtensionValue(cert, FRESHEST_CRL));
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException("Freshest CRL extension could not be decoded from certificate.", e);
+            }
+            if (freshestCRL == null)
+            {
+                try
+                {
+                    freshestCRL = CRLDistPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+                        FRESHEST_CRL));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException("Freshest CRL extension could not be decoded from CRL.", e);
+                }
+            }
+            if (freshestCRL != null)
+            {
+                try
+                {
+                    CertPathValidatorUtilities.addAdditionalStoresFromCRLDistributionPoint(freshestCRL, paramsPKIX);
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException(
+                        "No new delta CRL locations could be added from Freshest CRL extension.", e);
+                }
+                // get delta CRL(s)
+                try
+                {
+                    set.addAll(CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl));
+                }
+                catch (AnnotatedException e)
+                {
+                    throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+                }
+            }
+        }
+        return set;
+    }
+
+    protected static Set[] processCRLA1ii(
+        Date currentDate,
+        ExtendedPKIXParameters paramsPKIX,
+        X509Certificate cert,
+        X509CRL crl)
+        throws AnnotatedException
+    {
+        Set completeSet = new HashSet();
+        Set deltaSet = new HashSet();
+        X509CRLStoreSelector crlselect = new X509CRLStoreSelector();
+        crlselect.setCertificateChecking(cert);
+
+        if (paramsPKIX.getDate() != null)
+        {
+            crlselect.setDateAndTime(paramsPKIX.getDate());
+        }
+        else
+        {
+            crlselect.setDateAndTime(currentDate);
+        }
+
+        try
+        {
+            crlselect.addIssuerName(crl.getIssuerX500Principal().getEncoded());
+        }
+        catch (IOException e)
+        {
+            throw new AnnotatedException("Cannot extract issuer from CRL." + e, e);
+        }
+
+        crlselect.setCompleteCRLEnabled(true);
+
+        // get complete CRL(s)
+        try
+        {
+            completeSet.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getAdditionalStores()));
+            completeSet.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getStores()));
+            completeSet.addAll(CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getCertStores()));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException("Exception obtaining complete CRLs.", e);
+        }
+        if (paramsPKIX.isUseDeltasEnabled())
+        {
+            // get delta CRL(s)
+            try
+            {
+                deltaSet.addAll(CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl));
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+            }
+        }
+        return new Set[]
+            {
+                completeSet,
+                deltaSet};
+    }
+
+    /**
+     * If use-deltas is set, verify the issuer and scope of the delta CRL.
+     *
+     * @param deltaCRL    The delta CRL.
+     * @param completeCRL The complete CRL.
+     * @param pkixParams  The PKIX paramaters.
+     * @throws AnnotatedException if an exception occurs.
+     */
+    protected static void processCRLC(
+        X509CRL deltaCRL,
+        X509CRL completeCRL,
+        ExtendedPKIXParameters pkixParams)
+        throws AnnotatedException
+    {
+        if (deltaCRL == null)
+        {
+            return;
+        }
+        IssuingDistributionPoint completeidp = null;
+        try
+        {
+            completeidp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(
+                completeCRL, RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+        }
+
+        if (pkixParams.isUseDeltasEnabled())
+        {
+            // (c) (1)
+            if (!deltaCRL.getIssuerX500Principal().equals(completeCRL.getIssuerX500Principal()))
+            {
+                throw new AnnotatedException("Complete CRL issuer does not match delta CRL issuer.");
+            }
+
+            // (c) (2)
+            IssuingDistributionPoint deltaidp = null;
+            try
+            {
+                deltaidp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(
+                    deltaCRL, ISSUING_DISTRIBUTION_POINT));
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException(
+                    "Issuing distribution point extension from delta CRL could not be decoded.", e);
+            }
+
+            boolean match = false;
+            if (completeidp == null)
+            {
+                if (deltaidp == null)
+                {
+                    match = true;
+                }
+            }
+            else
+            {
+                if (completeidp.equals(deltaidp))
+                {
+                    match = true;
+                }
+            }
+            if (!match)
+            {
+                throw new AnnotatedException(
+                    "Issuing distribution point extension from delta CRL and complete CRL does not match.");
+            }
+
+            // (c) (3)
+            DERObject completeKeyIdentifier = null;
+            try
+            {
+                completeKeyIdentifier = CertPathValidatorUtilities.getExtensionValue(
+                    completeCRL, AUTHORITY_KEY_IDENTIFIER);
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException(
+                    "Authority key identifier extension could not be extracted from complete CRL.", e);
+            }
+
+            DERObject deltaKeyIdentifier = null;
+            try
+            {
+                deltaKeyIdentifier = CertPathValidatorUtilities.getExtensionValue(
+                    deltaCRL, AUTHORITY_KEY_IDENTIFIER);
+            }
+            catch (AnnotatedException e)
+            {
+                throw new AnnotatedException(
+                    "Authority key identifier extension could not be extracted from delta CRL.", e);
+            }
+
+            if (completeKeyIdentifier == null)
+            {
+                throw new AnnotatedException("CRL authority key identifier is null.");
+            }
+
+            if (deltaKeyIdentifier == null)
+            {
+                throw new AnnotatedException("Delta CRL authority key identifier is null.");
+            }
+
+            if (!completeKeyIdentifier.equals(deltaKeyIdentifier))
+            {
+                throw new AnnotatedException(
+                    "Delta CRL authority key identifier does not match complete CRL authority key identifier.");
+            }
+        }
+    }
+
+    protected static void processCRLI(
+        Date validDate,
+        X509CRL deltacrl,
+        Object cert,
+        CertStatus certStatus,
+        ExtendedPKIXParameters pkixParams)
+        throws AnnotatedException
+    {
+        if (pkixParams.isUseDeltasEnabled() && deltacrl != null)
+        {
+            CertPathValidatorUtilities.getCertStatus(validDate, deltacrl, cert, certStatus);
+        }
+    }
+
+    protected static void processCRLJ(
+        Date validDate,
+        X509CRL completecrl,
+        Object cert,
+        CertStatus certStatus)
+        throws AnnotatedException
+    {
+        if (certStatus.getCertStatus() == CertStatus.UNREVOKED)
+        {
+            CertPathValidatorUtilities.getCertStatus(validDate, completecrl, cert, certStatus);
+        }
+    }
+
+    protected static PKIXPolicyNode prepareCertB(
+        CertPath certPath,
+        int index,
+        List[] policyNodes,
+        PKIXPolicyNode validPolicyTree,
+        int policyMapping)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        int n = certs.size();
+        // i as defined in the algorithm description
+        int i = n - index;
+        // (b)
+        //
+        ASN1Sequence pm = null;
+        try
+        {
+            pm = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.POLICY_MAPPINGS));
+        }
+        catch (AnnotatedException ex)
+        {
+            throw new ExtCertPathValidatorException("Policy mappings extension could not be decoded.", ex, certPath,
+                index);
+        }
+        PKIXPolicyNode _validPolicyTree = validPolicyTree;
+        if (pm != null)
+        {
+            ASN1Sequence mappings = (ASN1Sequence)pm;
+            Map m_idp = new HashMap();
+            Set s_idp = new HashSet();
+
+            for (int j = 0; j < mappings.size(); j++)
+            {
+                ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j);
+                String id_p = ((DERObjectIdentifier)mapping.getObjectAt(0)).getId();
+                String sd_p = ((DERObjectIdentifier)mapping.getObjectAt(1)).getId();
+                Set tmp;
+
+                if (!m_idp.containsKey(id_p))
+                {
+                    tmp = new HashSet();
+                    tmp.add(sd_p);
+                    m_idp.put(id_p, tmp);
+                    s_idp.add(id_p);
+                }
+                else
+                {
+                    tmp = (Set)m_idp.get(id_p);
+                    tmp.add(sd_p);
+                }
+            }
+
+            Iterator it_idp = s_idp.iterator();
+            while (it_idp.hasNext())
+            {
+                String id_p = (String)it_idp.next();
+
+                //
+                // (1)
+                //
+                if (policyMapping > 0)
+                {
+                    boolean idp_found = false;
+                    Iterator nodes_i = policyNodes[i].iterator();
+                    while (nodes_i.hasNext())
+                    {
+                        PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+                        if (node.getValidPolicy().equals(id_p))
+                        {
+                            idp_found = true;
+                            node.expectedPolicies = (Set)m_idp.get(id_p);
+                            break;
+                        }
+                    }
+
+                    if (!idp_found)
+                    {
+                        nodes_i = policyNodes[i].iterator();
+                        while (nodes_i.hasNext())
+                        {
+                            PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+                            if (RFC3280CertPathUtilities.ANY_POLICY.equals(node.getValidPolicy()))
+                            {
+                                Set pq = null;
+                                ASN1Sequence policies = null;
+                                try
+                                {
+                                    policies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert,
+                                        RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+                                }
+                                catch (AnnotatedException e)
+                                {
+                                    throw new ExtCertPathValidatorException(
+                                        "Certificate policies extension could not be decoded.", e, certPath, index);
+                                }
+                                Enumeration e = policies.getObjects();
+                                while (e.hasMoreElements())
+                                {
+                                    PolicyInformation pinfo = null;
+                                    try
+                                    {
+                                        pinfo = PolicyInformation.getInstance(e.nextElement());
+                                    }
+                                    catch (Exception ex)
+                                    {
+                                        throw new CertPathValidatorException(
+                                            "Policy information could not be decoded.", ex, certPath, index);
+                                    }
+                                    if (RFC3280CertPathUtilities.ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId()))
+                                    {
+                                        try
+                                        {
+                                            pq = CertPathValidatorUtilities
+                                                .getQualifierSet(pinfo.getPolicyQualifiers());
+                                        }
+                                        catch (CertPathValidatorException ex)
+                                        {
+
+                                            throw new ExtCertPathValidatorException(
+                                                "Policy qualifier info set could not be decoded.", ex, certPath,
+                                                index);
+                                        }
+                                        break;
+                                    }
+                                }
+                                boolean ci = false;
+                                if (cert.getCriticalExtensionOIDs() != null)
+                                {
+                                    ci = cert.getCriticalExtensionOIDs().contains(
+                                        RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+                                }
+
+                                PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+                                if (RFC3280CertPathUtilities.ANY_POLICY.equals(p_node.getValidPolicy()))
+                                {
+                                    PKIXPolicyNode c_node = new PKIXPolicyNode(new ArrayList(), i, (Set)m_idp
+                                        .get(id_p), p_node, pq, id_p, ci);
+                                    p_node.addChild(c_node);
+                                    policyNodes[i].add(c_node);
+                                }
+                                break;
+                            }
+                        }
+                    }
+
+                    //
+                    // (2)
+                    //
+                }
+                else if (policyMapping <= 0)
+                {
+                    Iterator nodes_i = policyNodes[i].iterator();
+                    while (nodes_i.hasNext())
+                    {
+                        PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+                        if (node.getValidPolicy().equals(id_p))
+                        {
+                            PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+                            p_node.removeChild(node);
+                            nodes_i.remove();
+                            for (int k = (i - 1); k >= 0; k--)
+                            {
+                                List nodes = policyNodes[k];
+                                for (int l = 0; l < nodes.size(); l++)
+                                {
+                                    PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l);
+                                    if (!node2.hasChildren())
+                                    {
+                                        _validPolicyTree = CertPathValidatorUtilities.removePolicyNode(
+                                            _validPolicyTree, policyNodes, node2);
+                                        if (_validPolicyTree == null)
+                                        {
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return _validPolicyTree;
+    }
+
+    protected static void prepareNextCertA(
+        CertPath certPath,
+        int index)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        //
+        // (a) check the policy mappings
+        //
+        ASN1Sequence pm = null;
+        try
+        {
+            pm = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.POLICY_MAPPINGS));
+        }
+        catch (AnnotatedException ex)
+        {
+            throw new ExtCertPathValidatorException("Policy mappings extension could not be decoded.", ex, certPath,
+                index);
+        }
+        if (pm != null)
+        {
+            ASN1Sequence mappings = pm;
+
+            for (int j = 0; j < mappings.size(); j++)
+            {
+                DERObjectIdentifier issuerDomainPolicy = null;
+                DERObjectIdentifier subjectDomainPolicy = null;
+                try
+                {
+                    ASN1Sequence mapping = DERSequence.getInstance(mappings.getObjectAt(j));
+
+                    issuerDomainPolicy = DERObjectIdentifier.getInstance(mapping.getObjectAt(0));
+                    subjectDomainPolicy = DERObjectIdentifier.getInstance(mapping.getObjectAt(1));
+                }
+                catch (Exception e)
+                {
+                    throw new ExtCertPathValidatorException("Policy mappings extension contents could not be decoded.",
+                        e, certPath, index);
+                }
+
+                if (RFC3280CertPathUtilities.ANY_POLICY.equals(issuerDomainPolicy.getId()))
+                {
+
+                    throw new CertPathValidatorException("IssuerDomainPolicy is anyPolicy", null, certPath, index);
+                }
+
+                if (RFC3280CertPathUtilities.ANY_POLICY.equals(subjectDomainPolicy.getId()))
+                {
+
+                    throw new CertPathValidatorException("SubjectDomainPolicy is anyPolicy,", null, certPath, index);
+                }
+            }
+        }
+    }
+
+    protected static void processCertF(
+        CertPath certPath,
+        int index,
+        PKIXPolicyNode validPolicyTree,
+        int explicitPolicy)
+        throws CertPathValidatorException
+    {
+        //
+        // (f)
+        //
+        if (explicitPolicy <= 0 && validPolicyTree == null)
+        {
+            throw new ExtCertPathValidatorException("No valid policy tree found when one expected.", null, certPath,
+                index);
+        }
+    }
+
+    protected static PKIXPolicyNode processCertE(
+        CertPath certPath,
+        int index,
+        PKIXPolicyNode validPolicyTree)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        // 
+        // (e)
+        //
+        ASN1Sequence certPolicies = null;
+        try
+        {
+            certPolicies = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.CERTIFICATE_POLICIES));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new ExtCertPathValidatorException("Could not read certificate policies extension from certificate.",
+                e, certPath, index);
+        }
+        if (certPolicies == null)
+        {
+            validPolicyTree = null;
+        }
+        return validPolicyTree;
+    }
+
+    protected static void processCertBC(
+        CertPath certPath,
+        int index,
+        PKIXNameConstraintValidator nameConstraintValidator)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        int n = certs.size();
+        // i as defined in the algorithm description
+        int i = n - index;
+        //
+        // (b), (c) permitted and excluded subtree checking.
+        //
+        if (!(CertPathValidatorUtilities.isSelfIssued(cert) && (i < n)))
+        {
+            X500Principal principal = CertPathValidatorUtilities.getSubjectPrincipal(cert);
+            ASN1InputStream aIn = new ASN1InputStream(principal.getEncoded());
+            ASN1Sequence dns;
+
+            try
+            {
+                dns = DERSequence.getInstance(aIn.readObject());
+            }
+            catch (Exception e)
+            {
+                throw new CertPathValidatorException("Exception extracting subject name when checking subtrees.", e,
+                    certPath, index);
+            }
+
+            try
+            {
+                nameConstraintValidator.checkPermittedDN(dns);
+                nameConstraintValidator.checkExcludedDN(dns);
+            }
+            catch (PKIXNameConstraintValidatorException e)
+            {
+                throw new CertPathValidatorException("Subtree check for certificate subject failed.", e, certPath,
+                    index);
+            }
+
+            GeneralNames altName = null;
+            try
+            {
+                altName = GeneralNames.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                    RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME));
+            }
+            catch (Exception e)
+            {
+                throw new CertPathValidatorException("Subject alternative name extension could not be decoded.", e,
+                    certPath, index);
+            }
+            Vector emails = new X509Name(dns).getValues(X509Name.EmailAddress);
+            for (Enumeration e = emails.elements(); e.hasMoreElements();)
+            {
+                String email = (String)e.nextElement();
+                GeneralName emailAsGeneralName = new GeneralName(GeneralName.rfc822Name, email);
+                try
+                {
+                    nameConstraintValidator.checkPermitted(emailAsGeneralName);
+                    nameConstraintValidator.checkExcluded(emailAsGeneralName);
+                }
+                catch (PKIXNameConstraintValidatorException ex)
+                {
+                    throw new CertPathValidatorException(
+                        "Subtree check for certificate subject alternative email failed.", ex, certPath, index);
+                }
+            }
+            if (altName != null)
+            {
+                GeneralName[] genNames = null;
+                try
+                {
+                    genNames = altName.getNames();
+                }
+                catch (Exception e)
+                {
+                    throw new CertPathValidatorException("Subject alternative name contents could not be decoded.", e,
+                        certPath, index);
+                }
+                for (int j = 0; j < genNames.length; j++)
+                {
+
+                    try
+                    {
+                        nameConstraintValidator.checkPermitted(genNames[j]);
+                        nameConstraintValidator.checkExcluded(genNames[j]);
+                    }
+                    catch (PKIXNameConstraintValidatorException e)
+                    {
+                        throw new CertPathValidatorException(
+                            "Subtree check for certificate subject alternative name failed.", e, certPath, index);
+                    }
+                }
+            }
+        }
+    }
+
+    protected static PKIXPolicyNode processCertD(
+        CertPath certPath,
+        int index,
+        Set acceptablePolicies,
+        PKIXPolicyNode validPolicyTree,
+        List[] policyNodes,
+        int inhibitAnyPolicy)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        int n = certs.size();
+        // i as defined in the algorithm description
+        int i = n - index;
+        //
+        // (d) policy Information checking against initial policy and
+        // policy mapping
+        //
+        ASN1Sequence certPolicies = null;
+        try
+        {
+            certPolicies = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.CERTIFICATE_POLICIES));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new ExtCertPathValidatorException("Could not read certificate policies extension from certificate.",
+                e, certPath, index);
+        }
+        if (certPolicies != null && validPolicyTree != null)
+        {
+            //
+            // (d) (1)
+            //
+            Enumeration e = certPolicies.getObjects();
+            Set pols = new HashSet();
+
+            while (e.hasMoreElements())
+            {
+                PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
+                DERObjectIdentifier pOid = pInfo.getPolicyIdentifier();
+
+                pols.add(pOid.getId());
+
+                if (!RFC3280CertPathUtilities.ANY_POLICY.equals(pOid.getId()))
+                {
+                    Set pq = null;
+                    try
+                    {
+                        pq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
+                    }
+                    catch (CertPathValidatorException ex)
+                    {
+                        throw new ExtCertPathValidatorException("Policy qualifier info set could not be build.", ex,
+                            certPath, index);
+                    }
+
+                    boolean match = CertPathValidatorUtilities.processCertD1i(i, policyNodes, pOid, pq);
+
+                    if (!match)
+                    {
+                        CertPathValidatorUtilities.processCertD1ii(i, policyNodes, pOid, pq);
+                    }
+                }
+            }
+
+            if (acceptablePolicies.isEmpty() || acceptablePolicies.contains(RFC3280CertPathUtilities.ANY_POLICY))
+            {
+                acceptablePolicies.clear();
+                acceptablePolicies.addAll(pols);
+            }
+            else
+            {
+                Iterator it = acceptablePolicies.iterator();
+                Set t1 = new HashSet();
+
+                while (it.hasNext())
+                {
+                    Object o = it.next();
+
+                    if (pols.contains(o))
+                    {
+                        t1.add(o);
+                    }
+                }
+                acceptablePolicies.clear();
+                acceptablePolicies.addAll(t1);
+            }
+
+            //
+            // (d) (2)
+            //
+            if ((inhibitAnyPolicy > 0) || ((i < n) && CertPathValidatorUtilities.isSelfIssued(cert)))
+            {
+                e = certPolicies.getObjects();
+
+                while (e.hasMoreElements())
+                {
+                    PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
+
+                    if (RFC3280CertPathUtilities.ANY_POLICY.equals(pInfo.getPolicyIdentifier().getId()))
+                    {
+                        Set _apq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
+                        List _nodes = policyNodes[i - 1];
+
+                        for (int k = 0; k < _nodes.size(); k++)
+                        {
+                            PKIXPolicyNode _node = (PKIXPolicyNode)_nodes.get(k);
+
+                            Iterator _policySetIter = _node.getExpectedPolicies().iterator();
+                            while (_policySetIter.hasNext())
+                            {
+                                Object _tmp = _policySetIter.next();
+
+                                String _policy;
+                                if (_tmp instanceof String)
+                                {
+                                    _policy = (String)_tmp;
+                                }
+                                else if (_tmp instanceof DERObjectIdentifier)
+                                {
+                                    _policy = ((DERObjectIdentifier)_tmp).getId();
+                                }
+                                else
+                                {
+                                    continue;
+                                }
+
+                                boolean _found = false;
+                                Iterator _childrenIter = _node.getChildren();
+
+                                while (_childrenIter.hasNext())
+                                {
+                                    PKIXPolicyNode _child = (PKIXPolicyNode)_childrenIter.next();
+
+                                    if (_policy.equals(_child.getValidPolicy()))
+                                    {
+                                        _found = true;
+                                    }
+                                }
+
+                                if (!_found)
+                                {
+                                    Set _newChildExpectedPolicies = new HashSet();
+                                    _newChildExpectedPolicies.add(_policy);
+
+                                    PKIXPolicyNode _newChild = new PKIXPolicyNode(new ArrayList(), i,
+                                        _newChildExpectedPolicies, _node, _apq, _policy, false);
+                                    _node.addChild(_newChild);
+                                    policyNodes[i].add(_newChild);
+                                }
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+
+            PKIXPolicyNode _validPolicyTree = validPolicyTree;
+            //
+            // (d) (3)
+            //
+            for (int j = (i - 1); j >= 0; j--)
+            {
+                List nodes = policyNodes[j];
+
+                for (int k = 0; k < nodes.size(); k++)
+                {
+                    PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+                    if (!node.hasChildren())
+                    {
+                        _validPolicyTree = CertPathValidatorUtilities.removePolicyNode(_validPolicyTree, policyNodes,
+                            node);
+                        if (_validPolicyTree == null)
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+
+            //
+            // d (4)
+            //
+            Set criticalExtensionOids = cert.getCriticalExtensionOIDs();
+
+            if (criticalExtensionOids != null)
+            {
+                boolean critical = criticalExtensionOids.contains(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+
+                List nodes = policyNodes[i];
+                for (int j = 0; j < nodes.size(); j++)
+                {
+                    PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(j);
+                    node.setCritical(critical);
+                }
+            }
+            return _validPolicyTree;
+        }
+        return null;
+    }
+
+    protected static void processCertA(
+        CertPath certPath,
+        ExtendedPKIXParameters paramsPKIX,
+        int index,
+        PublicKey workingPublicKey,
+        boolean verificationAlreadyPerformed,
+        X500Principal workingIssuerName,
+        X509Certificate sign,
+        // BEGIN android-added
+        int i, 
+        boolean trustAnchorInChain)
+        // END android-added
+        throws ExtCertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (a) verify
+        //
+        if (!verificationAlreadyPerformed)
+        {
+            try
+            {
+                // (a) (1)
+                //
+                // BEGIN android-added
+                if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
+                {
+                // END android-added
+                CertPathValidatorUtilities.verifyX509Certificate(cert, workingPublicKey,
+                    paramsPKIX.getSigProvider());
+                // BEGIN android-added
+                }
+                // END android-added
+            }
+            catch (GeneralSecurityException e)
+            {
+                throw new ExtCertPathValidatorException("Could not validate certificate signature.", e, certPath, index);
+            }
+        }
+
+        try
+        {
+            // (a) (2)
+            //
+            cert.checkValidity(CertPathValidatorUtilities
+                .getValidCertDateFromValidityModel(paramsPKIX, certPath, index));
+        }
+        catch (CertificateExpiredException e)
+        {
+            throw new ExtCertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
+        }
+        catch (CertificateNotYetValidException e)
+        {
+            throw new ExtCertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
+        }
+        catch (AnnotatedException e)
+        {
+            throw new ExtCertPathValidatorException("Could not validate time of certificate.", e, certPath, index);
+        }
+
+        //
+        // (a) (3)
+        //
+        if (paramsPKIX.isRevocationEnabled())
+        {
+            try
+            {
+                checkCRLs(paramsPKIX, cert, CertPathValidatorUtilities.getValidCertDateFromValidityModel(paramsPKIX,
+                    certPath, index), sign, workingPublicKey, certs);
+            }
+            catch (AnnotatedException e)
+            {
+                Throwable cause = e;
+                if (null != e.getCause())
+                {
+                    cause = e.getCause();
+                }
+                throw new ExtCertPathValidatorException(e.getMessage(), cause, certPath, index);
+            }
+        }
+
+        //
+        // (a) (4) name chaining
+        //
+        if (!CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).equals(workingIssuerName))
+        {
+            throw new ExtCertPathValidatorException("IssuerName(" + CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert)
+                + ") does not match SubjectName(" + workingIssuerName + ") of signing certificate.", null,
+                certPath, index);
+        }
+    }
+
+    protected static int prepareNextCertI1(
+        CertPath certPath,
+        int index,
+        int explicitPolicy)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (i)
+        //
+        ASN1Sequence pc = null;
+        try
+        {
+            pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Policy constraints extension cannot be decoded.", e, certPath,
+                index);
+        }
+
+        int tmpInt;
+
+        if (pc != null)
+        {
+            Enumeration policyConstraints = pc.getObjects();
+
+            while (policyConstraints.hasMoreElements())
+            {
+                try
+                {
+
+                    ASN1TaggedObject constraint = ASN1TaggedObject.getInstance(policyConstraints.nextElement());
+                    if (constraint.getTagNo() == 0)
+                    {
+                        tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
+                        if (tmpInt < explicitPolicy)
+                        {
+                            return tmpInt;
+                        }
+                        break;
+                    }
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw new ExtCertPathValidatorException("Policy constraints extension contents cannot be decoded.",
+                        e, certPath, index);
+                }
+            }
+        }
+        return explicitPolicy;
+    }
+
+    protected static int prepareNextCertI2(
+        CertPath certPath,
+        int index,
+        int policyMapping)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (i)
+        //
+        ASN1Sequence pc = null;
+        try
+        {
+            pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Policy constraints extension cannot be decoded.", e, certPath,
+                index);
+        }
+
+        int tmpInt;
+
+        if (pc != null)
+        {
+            Enumeration policyConstraints = pc.getObjects();
+
+            while (policyConstraints.hasMoreElements())
+            {
+                try
+                {
+                    ASN1TaggedObject constraint = ASN1TaggedObject.getInstance(policyConstraints.nextElement());
+                    if (constraint.getTagNo() == 1)
+                    {
+                        tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
+                        if (tmpInt < policyMapping)
+                        {
+                            return tmpInt;
+                        }
+                        break;
+                    }
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw new ExtCertPathValidatorException("Policy constraints extension contents cannot be decoded.",
+                        e, certPath, index);
+                }
+            }
+        }
+        return policyMapping;
+    }
+
+    protected static void prepareNextCertG(
+        CertPath certPath,
+        int index,
+        PKIXNameConstraintValidator nameConstraintValidator)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (g) handle the name constraints extension
+        //
+        NameConstraints nc = null;
+        try
+        {
+            ASN1Sequence ncSeq = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.NAME_CONSTRAINTS));
+            if (ncSeq != null)
+            {
+                nc = new NameConstraints(ncSeq);
+            }
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Name constraints extension could not be decoded.", e, certPath,
+                index);
+        }
+        if (nc != null)
+        {
+
+            //
+            // (g) (1) permitted subtrees
+            //
+            ASN1Sequence permitted = nc.getPermittedSubtrees();
+            if (permitted != null)
+            {
+                try
+                {
+                    nameConstraintValidator.intersectPermittedSubtree(permitted);
+                }
+                catch (Exception ex)
+                {
+                    throw new ExtCertPathValidatorException(
+                        "Permitted subtrees cannot be build from name constraints extension.", ex, certPath, index);
+                }
+            }
+
+            //
+            // (g) (2) excluded subtrees
+            //
+            ASN1Sequence excluded = nc.getExcludedSubtrees();
+            if (excluded != null)
+            {
+                Enumeration e = excluded.getObjects();
+                try
+                {
+                    while (e.hasMoreElements())
+                    {
+                        GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement());
+                        nameConstraintValidator.addExcludedSubtree(subtree);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    throw new ExtCertPathValidatorException(
+                        "Excluded subtrees cannot be build from name constraints extension.", ex, certPath, index);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks a distribution point for revocation information for the
+     * certificate <code>cert</code>.
+     *
+     * @param dp                 The distribution point to consider.
+     * @param paramsPKIX         PKIX parameters.
+     * @param cert               Certificate to check if it is revoked.
+     * @param validDate          The date when the certificate revocation status should be
+     *                           checked.
+     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+     * @param defaultCRLSignKey  The public key of the issuer certificate
+     *                           <code>defaultCRLSignCert</code>.
+     * @param certStatus         The current certificate revocation status.
+     * @param reasonMask         The reasons mask which is already checked.
+     * @param certPathCerts      The certificates of the certification path.
+     * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+     *                            or some error occurs.
+     */
+    private static void checkCRL(
+        DistributionPoint dp,
+        ExtendedPKIXParameters paramsPKIX,
+        X509Certificate cert,
+        Date validDate,
+        X509Certificate defaultCRLSignCert,
+        PublicKey defaultCRLSignKey,
+        CertStatus certStatus,
+        ReasonsMask reasonMask,
+        List certPathCerts)
+        throws AnnotatedException
+    {
+        Date currentDate = new Date(System.currentTimeMillis());
+        if (validDate.getTime() > currentDate.getTime())
+        {
+            throw new AnnotatedException("Validation time is in future.");
+        }
+
+        // (a)
+        /*
+         * We always get timely valid CRLs, so there is no step (a) (1).
+         * "locally cached" CRLs are assumed to be in getStore(), additional
+         * CRLs must be enabled in the ExtendedPKIXParameters and are in
+         * getAdditionalStore()
+         */
+
+        Set crls = CertPathValidatorUtilities.getCompleteCRLs(dp, cert, currentDate, paramsPKIX);
+        boolean validCrlFound = false;
+        AnnotatedException lastException = null;
+        Iterator crl_iter = crls.iterator();
+
+        while (crl_iter.hasNext() && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonMask.isAllReasons())
+        {
+            try
+            {
+                X509CRL crl = (X509CRL)crl_iter.next();
+
+                // (d)
+                ReasonsMask interimReasonsMask = RFC3280CertPathUtilities.processCRLD(crl, dp);
+
+                // (e)
+                /*
+                 * The reasons mask is updated at the end, so only valid CRLs
+                 * can update it. If this CRL does not contain new reasons it
+                 * must be ignored.
+                 */
+                if (!interimReasonsMask.hasNewReasons(reasonMask))
+                {
+                    continue;
+                }
+
+                // (f)
+                Set keys = RFC3280CertPathUtilities.processCRLF(crl, cert, defaultCRLSignCert, defaultCRLSignKey,
+                    paramsPKIX, certPathCerts);
+                // (g)
+                PublicKey key = RFC3280CertPathUtilities.processCRLG(crl, keys);
+
+                X509CRL deltaCRL = null;
+
+                if (paramsPKIX.isUseDeltasEnabled())
+                {
+                    // get delta CRLs
+                    Set deltaCRLs = CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl);
+                    // we only want one valid delta CRL
+                    // (h)
+                    deltaCRL = RFC3280CertPathUtilities.processCRLH(deltaCRLs, key);
+                }
+
+                /*
+                 * CRL must be be valid at the current time, not the validation
+                 * time. If a certificate is revoked with reason keyCompromise,
+                 * cACompromise, it can be used for forgery, also for the past.
+                 * This reason may not be contained in older CRLs.
+                 */
+
+                /*
+                 * in the chain model signatures stay valid also after the
+                 * certificate has been expired, so they do not have to be in
+                 * the CRL validity time
+                 */
+
+                if (paramsPKIX.getValidityModel() != ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL)
+                {
+                    /*
+                     * if a certificate has expired, but was revoked, it is not
+                     * more in the CRL, so it would be regarded as valid if the
+                     * first check is not done
+                     */
+                    if (cert.getNotAfter().getTime() < crl.getThisUpdate().getTime())
+                    {
+                        throw new AnnotatedException("No valid CRL for current time found.");
+                    }
+                }
+
+                RFC3280CertPathUtilities.processCRLB1(dp, cert, crl);
+
+                // (b) (2)
+                RFC3280CertPathUtilities.processCRLB2(dp, cert, crl);
+
+                // (c)
+                RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX);
+
+                // (i)
+                RFC3280CertPathUtilities.processCRLI(validDate, deltaCRL, cert, certStatus, paramsPKIX);
+
+                // (j)
+                RFC3280CertPathUtilities.processCRLJ(validDate, crl, cert, certStatus);
+
+                // (k)
+                if (certStatus.getCertStatus() == CRLReason.removeFromCRL)
+                {
+                    certStatus.setCertStatus(CertStatus.UNREVOKED);
+                }
+
+                // update reasons mask
+                reasonMask.addReasons(interimReasonsMask);
+
+                Set criticalExtensions = crl.getCriticalExtensionOIDs();
+                if (criticalExtensions != null)
+                {
+                    criticalExtensions = new HashSet(criticalExtensions);
+                    criticalExtensions.remove(X509Extensions.IssuingDistributionPoint.getId());
+                    criticalExtensions.remove(X509Extensions.DeltaCRLIndicator.getId());
+
+                    if (!criticalExtensions.isEmpty())
+                    {
+                        throw new AnnotatedException("CRL contains unsupported critical extensions.");
+                    }
+                }
+
+                if (deltaCRL != null)
+                {
+                    criticalExtensions = deltaCRL.getCriticalExtensionOIDs();
+                    if (criticalExtensions != null)
+                    {
+                        criticalExtensions = new HashSet(criticalExtensions);
+                        criticalExtensions.remove(X509Extensions.IssuingDistributionPoint.getId());
+                        criticalExtensions.remove(X509Extensions.DeltaCRLIndicator.getId());
+                        if (!criticalExtensions.isEmpty())
+                        {
+                            throw new AnnotatedException("Delta CRL contains unsupported critical extension.");
+                        }
+                    }
+                }
+
+                validCrlFound = true;
+            }
+            catch (AnnotatedException e)
+            {
+                lastException = e;
+            }
+        }
+        if (!validCrlFound)
+        {
+            throw lastException;
+        }
+    }
+
+    /**
+     * Checks a certificate if it is revoked.
+     *
+     * @param paramsPKIX       PKIX parameters.
+     * @param cert             Certificate to check if it is revoked.
+     * @param validDate        The date when the certificate revocation status should be
+     *                         checked.
+     * @param sign             The issuer certificate of the certificate <code>cert</code>.
+     * @param workingPublicKey The public key of the issuer certificate <code>sign</code>.
+     * @param certPathCerts    The certificates of the certification path.
+     * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+     *                            or some error occurs.
+     */
+    protected static void checkCRLs(
+        ExtendedPKIXParameters paramsPKIX,
+        X509Certificate cert,
+        Date validDate,
+        X509Certificate sign,
+        PublicKey workingPublicKey,
+        List certPathCerts)
+        throws AnnotatedException
+    {
+        AnnotatedException lastException = null;
+        CRLDistPoint crldp = null;
+        try
+        {
+            crldp = CRLDistPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS));
+        }
+        catch (Exception e)
+        {
+            throw new AnnotatedException("CRL distribution point extension could not be read.", e);
+        }
+        try
+        {
+            CertPathValidatorUtilities.addAdditionalStoresFromCRLDistributionPoint(crldp, paramsPKIX);
+        }
+        catch (AnnotatedException e)
+        {
+            throw new AnnotatedException(
+                "No additional CRL locations could be decoded from CRL distribution point extension.", e);
+        }
+        CertStatus certStatus = new CertStatus();
+        ReasonsMask reasonsMask = new ReasonsMask();
+
+        boolean validCrlFound = false;
+        // for each distribution point
+        if (crldp != null)
+        {
+            DistributionPoint dps[] = null;
+            try
+            {
+                dps = crldp.getDistributionPoints();
+            }
+            catch (Exception e)
+            {
+                throw new AnnotatedException("Distribution points could not be read.", e);
+            }
+            if (dps != null)
+            {
+                for (int i = 0; i < dps.length && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons(); i++)
+                {
+                    ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters)paramsPKIX.clone();
+                    try
+                    {
+                        checkCRL(dps[i], paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, certPathCerts);
+                        validCrlFound = true;
+                    }
+                    catch (AnnotatedException e)
+                    {
+                        lastException = e;
+                    }
+                }
+            }
+        }
+
+        /*
+         * If the revocation status has not been determined, repeat the process
+         * above with any available CRLs not specified in a distribution point
+         * but issued by the certificate issuer.
+         */
+
+        if (certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons())
+        {
+            try
+            {
+                /*
+                 * assume a DP with both the reasons and the cRLIssuer fields
+                 * omitted and a distribution point name of the certificate
+                 * issuer.
+                 */
+                DERObject issuer = null;
+                try
+                {
+                    issuer = new ASN1InputStream(CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).getEncoded())
+                        .readObject();
+                }
+                catch (Exception e)
+                {
+                    throw new AnnotatedException("Issuer from certificate for CRL could not be reencoded.", e);
+                }
+                DistributionPoint dp = new DistributionPoint(new DistributionPointName(0, new GeneralNames(
+                    new GeneralName(GeneralName.directoryName, issuer))), null, null);
+                ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters)paramsPKIX.clone();
+                checkCRL(dp, paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask,
+                    certPathCerts);
+                validCrlFound = true;
+            }
+            catch (AnnotatedException e)
+            {
+                lastException = e;
+            }
+        }
+
+        if (!validCrlFound)
+        {
+            if (lastException instanceof AnnotatedException)
+            {
+                throw lastException;
+            }
+
+            throw new AnnotatedException("No valid CRL found.", lastException);
+        }
+        if (certStatus.getCertStatus() != CertStatus.UNREVOKED)
+        {
+            String message = "Certificate revocation after " + certStatus.getRevocationDate();
+            message += ", reason: " + crlReasons[certStatus.getCertStatus()];
+            throw new AnnotatedException(message);
+        }
+        if (!reasonsMask.isAllReasons() && certStatus.getCertStatus() == CertStatus.UNREVOKED)
+        {
+            certStatus.setCertStatus(CertStatus.UNDETERMINED);
+        }
+        if (certStatus.getCertStatus() == CertStatus.UNDETERMINED)
+        {
+            throw new AnnotatedException("Certificate status could not be determined.");
+        }
+    }
+
+    protected static int prepareNextCertJ(
+        CertPath certPath,
+        int index,
+        int inhibitAnyPolicy)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (j)
+        //
+        DERInteger iap = null;
+        try
+        {
+            iap = DERInteger.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.INHIBIT_ANY_POLICY));
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Inhibit any-policy extension cannot be decoded.", e, certPath,
+                index);
+        }
+
+        if (iap != null)
+        {
+            int _inhibitAnyPolicy = iap.getValue().intValue();
+
+            if (_inhibitAnyPolicy < inhibitAnyPolicy)
+            {
+                return _inhibitAnyPolicy;
+            }
+        }
+        return inhibitAnyPolicy;
+    }
+
+    protected static void prepareNextCertK(
+        CertPath certPath,
+        int index,
+        // BEGIN android-added
+        int i, 
+        boolean trustAnchorInChain)
+        // END android-added
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (k)
+        //
+        BasicConstraints bc = null;
+        try
+        {
+            bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.BASIC_CONSTRAINTS));
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+                index);
+        }
+        if (bc != null)
+        {
+            if (!(bc.isCA()))
+            {
+                throw new CertPathValidatorException("Not a CA certificate");
+            }
+        }
+        else
+        {
+            // BEGIN android-added
+            if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate
+            {
+            // END android-added
+            throw new CertPathValidatorException("Intermediate certificate lacks BasicConstraints");
+            // BEGIN android-added
+            }
+            // END android-added
+        }
+    }
+
+    protected static int prepareNextCertL(
+        CertPath certPath,
+        int index,
+        int maxPathLength)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (l)
+        //
+        if (!CertPathValidatorUtilities.isSelfIssued(cert))
+        {
+            if (maxPathLength <= 0)
+            {
+                throw new ExtCertPathValidatorException("Max path length not greater than zero", null, certPath, index);
+            }
+
+            return maxPathLength - 1;
+        }
+        return maxPathLength;
+    }
+
+    protected static int prepareNextCertM(
+        CertPath certPath,
+        int index,
+        int maxPathLength)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+
+        //
+        // (m)
+        //
+        BasicConstraints bc = null;
+        try
+        {
+            bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.BASIC_CONSTRAINTS));
+        }
+        catch (Exception e)
+        {
+            throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+                index);
+        }
+        if (bc != null)
+        {
+            BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
+
+            if (_pathLengthConstraint != null)
+            {
+                int _plc = _pathLengthConstraint.intValue();
+
+                if (_plc < maxPathLength)
+                {
+                    return _plc;
+                }
+            }
+        }
+        return maxPathLength;
+    }
+
+    protected static void prepareNextCertN(
+        CertPath certPath,
+        int index)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+
+        //
+        // (n)
+        //
+        boolean[] _usage = cert.getKeyUsage();
+
+        if ((_usage != null) && !_usage[RFC3280CertPathUtilities.KEY_CERT_SIGN])
+        {
+            throw new ExtCertPathValidatorException(
+                "Issuer certificate keyusage extension is critical and does not permit key signing.", null,
+                certPath, index);
+        }
+    }
+
+    protected static void prepareNextCertO(
+        CertPath certPath,
+        int index,
+        Set criticalExtensions,
+        List pathCheckers)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (o)
+        //
+
+        Iterator tmpIter;
+        tmpIter = pathCheckers.iterator();
+        while (tmpIter.hasNext())
+        {
+            try
+            {
+                ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
+            }
+            catch (CertPathValidatorException e)
+            {
+                throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index);
+            }
+        }
+        if (!criticalExtensions.isEmpty())
+        {
+            throw new ExtCertPathValidatorException("Certificate has unsupported critical extension.", null, certPath,
+                index);
+        }
+    }
+
+    protected static int prepareNextCertH1(
+        CertPath certPath,
+        int index,
+        int explicitPolicy)
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (h)
+        //
+        if (!CertPathValidatorUtilities.isSelfIssued(cert))
+        {
+            //
+            // (1)
+            //
+            if (explicitPolicy != 0)
+            {
+                return explicitPolicy - 1;
+            }
+        }
+        return explicitPolicy;
+    }
+
+    protected static int prepareNextCertH2(
+        CertPath certPath,
+        int index,
+        int policyMapping)
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (h)
+        //
+        if (!CertPathValidatorUtilities.isSelfIssued(cert))
+        {
+            //
+            // (2)
+            //
+            if (policyMapping != 0)
+            {
+                return policyMapping - 1;
+            }
+        }
+        return policyMapping;
+    }
+
+    protected static int prepareNextCertH3(
+        CertPath certPath,
+        int index,
+        int inhibitAnyPolicy)
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (h)
+        //
+        if (!CertPathValidatorUtilities.isSelfIssued(cert))
+        {
+            //
+            // (3)
+            //
+            if (inhibitAnyPolicy != 0)
+            {
+                return inhibitAnyPolicy - 1;
+            }
+        }
+        return inhibitAnyPolicy;
+    }
+
+    protected static final String[] crlReasons = new String[]
+        {
+            "unspecified",
+            "keyCompromise",
+            "cACompromise",
+            "affiliationChanged",
+            "superseded",
+            "cessationOfOperation",
+            "certificateHold",
+            "unknown",
+            "removeFromCRL",
+            "privilegeWithdrawn",
+            "aACompromise"};
+
+    protected static int wrapupCertA(
+        int explicitPolicy,
+        X509Certificate cert)
+    {
+        //
+        // (a)
+        //
+        if (!CertPathValidatorUtilities.isSelfIssued(cert) && (explicitPolicy != 0))
+        {
+            explicitPolicy--;
+        }
+        return explicitPolicy;
+    }
+
+    protected static int wrapupCertB(
+        CertPath certPath,
+        int index,
+        int explicitPolicy)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        //
+        // (b)
+        //
+        int tmpInt;
+        ASN1Sequence pc = null;
+        try
+        {
+            pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+                RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+        }
+        catch (AnnotatedException e)
+        {
+            throw new ExtCertPathValidatorException("Policy constraints could no be decoded.", e, certPath, index);
+        }
+        if (pc != null)
+        {
+            Enumeration policyConstraints = pc.getObjects();
+
+            while (policyConstraints.hasMoreElements())
+            {
+                ASN1TaggedObject constraint = (ASN1TaggedObject)policyConstraints.nextElement();
+                switch (constraint.getTagNo())
+                {
+                    case 0:
+                        try
+                        {
+                            tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
+                        }
+                        catch (Exception e)
+                        {
+                            throw new ExtCertPathValidatorException(
+                                "Policy constraints requireExplicitPolicy field could no be decoded.", e, certPath,
+                                index);
+                        }
+                        if (tmpInt == 0)
+                        {
+                            return 0;
+                        }
+                        break;
+                }
+            }
+        }
+        return explicitPolicy;
+    }
+
+    protected static void wrapupCertF(
+        CertPath certPath,
+        int index,
+        List pathCheckers,
+        Set criticalExtensions)
+        throws CertPathValidatorException
+    {
+        List certs = certPath.getCertificates();
+        X509Certificate cert = (X509Certificate)certs.get(index);
+        Iterator tmpIter;
+        tmpIter = pathCheckers.iterator();
+        while (tmpIter.hasNext())
+        {
+            try
+            {
+                ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
+            }
+            catch (CertPathValidatorException e)
+            {
+                throw new ExtCertPathValidatorException("Additional certificate path checker failed.", e, certPath,
+                    index);
+            }
+        }
+
+        if (!criticalExtensions.isEmpty())
+        {
+            throw new ExtCertPathValidatorException("Certificate has unsupported critical extension", null, certPath,
+                index);
+        }
+    }
+
+    protected static PKIXPolicyNode wrapupCertG(
+        CertPath certPath,
+        ExtendedPKIXParameters paramsPKIX,
+        Set userInitialPolicySet,
+        int index,
+        List[] policyNodes,
+        PKIXPolicyNode validPolicyTree,
+        Set acceptablePolicies)
+        throws CertPathValidatorException
+    {
+        int n = certPath.getCertificates().size();
+        //
+        // (g)
+        //
+        PKIXPolicyNode intersection;
+
+        //
+        // (g) (i)
+        //
+        if (validPolicyTree == null)
+        {
+            if (paramsPKIX.isExplicitPolicyRequired())
+            {
+                throw new ExtCertPathValidatorException("Explicit policy requested but none available.", null,
+                    certPath, index);
+            }
+            intersection = null;
+        }
+        else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g)
+        // (ii)
+        {
+            if (paramsPKIX.isExplicitPolicyRequired())
+            {
+                if (acceptablePolicies.isEmpty())
+                {
+                    throw new ExtCertPathValidatorException("Explicit policy requested but none available.", null,
+                        certPath, index);
+                }
+                else
+                {
+                    Set _validPolicyNodeSet = new HashSet();
+
+                    for (int j = 0; j < policyNodes.length; j++)
+                    {
+                        List _nodeDepth = policyNodes[j];
+
+                        for (int k = 0; k < _nodeDepth.size(); k++)
+                        {
+                            PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
+
+                            if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy()))
+                            {
+                                Iterator _iter = _node.getChildren();
+                                while (_iter.hasNext())
+                                {
+                                    _validPolicyNodeSet.add(_iter.next());
+                                }
+                            }
+                        }
+                    }
+
+                    Iterator _vpnsIter = _validPolicyNodeSet.iterator();
+                    while (_vpnsIter.hasNext())
+                    {
+                        PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
+                        String _validPolicy = _node.getValidPolicy();
+
+                        if (!acceptablePolicies.contains(_validPolicy))
+                        {
+                            // validPolicyTree =
+                            // removePolicyNode(validPolicyTree, policyNodes,
+                            // _node);
+                        }
+                    }
+                    if (validPolicyTree != null)
+                    {
+                        for (int j = (n - 1); j >= 0; j--)
+                        {
+                            List nodes = policyNodes[j];
+
+                            for (int k = 0; k < nodes.size(); k++)
+                            {
+                                PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+                                if (!node.hasChildren())
+                                {
+                                    validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree,
+                                        policyNodes, node);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            intersection = validPolicyTree;
+        }
+        else
+        {
+            //
+            // (g) (iii)
+            //
+            // This implementation is not exactly same as the one described in
+            // RFC3280.
+            // However, as far as the validation result is concerned, both
+            // produce
+            // adequate result. The only difference is whether AnyPolicy is
+            // remain
+            // in the policy tree or not.
+            //
+            // (g) (iii) 1
+            //
+            Set _validPolicyNodeSet = new HashSet();
+
+            for (int j = 0; j < policyNodes.length; j++)
+            {
+                List _nodeDepth = policyNodes[j];
+
+                for (int k = 0; k < _nodeDepth.size(); k++)
+                {
+                    PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
+
+                    if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy()))
+                    {
+                        Iterator _iter = _node.getChildren();
+                        while (_iter.hasNext())
+                        {
+                            PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next();
+                            if (!RFC3280CertPathUtilities.ANY_POLICY.equals(_c_node.getValidPolicy()))
+                            {
+                                _validPolicyNodeSet.add(_c_node);
+                            }
+                        }
+                    }
+                }
+            }
+
+            //
+            // (g) (iii) 2
+            //
+            Iterator _vpnsIter = _validPolicyNodeSet.iterator();
+            while (_vpnsIter.hasNext())
+            {
+                PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
+                String _validPolicy = _node.getValidPolicy();
+
+                if (!userInitialPolicySet.contains(_validPolicy))
+                {
+                    validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, _node);
+                }
+            }
+
+            //
+            // (g) (iii) 4
+            //
+            if (validPolicyTree != null)
+            {
+                for (int j = (n - 1); j >= 0; j--)
+                {
+                    List nodes = policyNodes[j];
+
+                    for (int k = 0; k < nodes.size(); k++)
+                    {
+                        PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+                        if (!node.hasChildren())
+                        {
+                            validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes,
+                                node);
+                        }
+                    }
+                }
+            }
+
+            intersection = validPolicyTree;
+        }
+        return intersection;
+    }
+
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java b/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
new file mode 100644
index 0000000..aeb4892
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.asn1.x509.ReasonFlags;
+
+/**
+ * This class helps to handle CRL revocation reasons mask. Each CRL handles a
+ * certain set of revocation reasons.
+ */
+class ReasonsMask
+{
+    private int _reasons;
+
+    /**
+     * Constructs are reason mask with the reasons.
+     * 
+     * @param reasons The reasons.
+     */
+    ReasonsMask(int reasons)
+    {
+        _reasons = reasons;
+    }
+
+    /**
+     * A reason mask with no reason.
+     * 
+     */
+    ReasonsMask()
+    {
+        this(0);
+    }
+
+    /**
+     * A mask with all revocation reasons.
+     */
+    static final ReasonsMask allReasons = new ReasonsMask(ReasonFlags.aACompromise
+            | ReasonFlags.affiliationChanged | ReasonFlags.cACompromise
+            | ReasonFlags.certificateHold | ReasonFlags.cessationOfOperation
+            | ReasonFlags.keyCompromise | ReasonFlags.privilegeWithdrawn
+            | ReasonFlags.unused | ReasonFlags.superseded);
+
+    /**
+     * Adds all reasons from the reasons mask to this mask.
+     * 
+     * @param mask The reasons mask to add.
+     */
+    void addReasons(ReasonsMask mask)
+    {
+        _reasons = _reasons | mask.getReasons();
+    }
+
+    /**
+     * Returns <code>true</code> if this reasons mask contains all possible
+     * reasons.
+     * 
+     * @return <code>true</code> if this reasons mask contains all possible
+     *         reasons.
+     */
+    boolean isAllReasons()
+    {
+        return _reasons == allReasons._reasons ? true : false;
+    }
+
+    /**
+     * Intersects this mask with the given reasons mask.
+     * 
+     * @param mask The mask to intersect with.
+     * @return The intersection of this and teh given mask.
+     */
+    ReasonsMask intersect(ReasonsMask mask)
+    {
+        ReasonsMask _mask = new ReasonsMask();
+        _mask.addReasons(new ReasonsMask(_reasons & mask.getReasons()));
+        return _mask;
+    }
+
+    /**
+     * Returns <code>true</code> if the passed reasons mask has new reasons.
+     * 
+     * @param mask The reasons mask which should be tested for new reasons.
+     * @return <code>true</code> if the passed reasons mask has new reasons.
+     */
+    boolean hasNewReasons(ReasonsMask mask)
+    {
+        return ((_reasons | mask.getReasons() ^ _reasons) != 0);
+    }
+
+    /**
+     * Returns the reasons in this mask.
+     * 
+     * @return Returns the reasons.
+     */
+    int getReasons()
+    {
+        return _reasons;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/WrapCipherSpi.java b/src/main/java/org/bouncycastle/jce/provider/WrapCipherSpi.java
index 5ac8938..f2cdc73 100644
--- a/src/main/java/org/bouncycastle/jce/provider/WrapCipherSpi.java
+++ b/src/main/java/org/bouncycastle/jce/provider/WrapCipherSpi.java
@@ -1,5 +1,37 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.DESedeWrapEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.RC2WrapEngine;
+// import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
+// END android-removed
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+// BEGIN android-removed
+// import javax.crypto.spec.RC2ParameterSpec;
+// END android-removed
+import javax.crypto.spec.RC5ParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -14,36 +46,6 @@
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.CipherSpi;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-import javax.crypto.spec.RC5ParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.Wrapper;
-import org.bouncycastle.crypto.engines.AESWrapEngine;
-import org.bouncycastle.crypto.engines.DESedeWrapEngine;
-// BEGIN android-removed
-// import org.bouncycastle.crypto.engines.RC2WrapEngine;
-// END android-removed
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-
 public abstract class WrapCipherSpi extends CipherSpi
     implements PBE
 {
@@ -54,7 +56,9 @@
                                     {
                                         IvParameterSpec.class,
                                         PBEParameterSpec.class,
-                                        RC2ParameterSpec.class,
+                                        // BEGIN android-removed
+                                        // RC2ParameterSpec.class,
+                                        // END android-removed
                                         RC5ParameterSpec.class
                                     };
 
@@ -65,7 +69,10 @@
 
     protected AlgorithmParameters     engineParams = null;
 
-    protected Wrapper                   wrapEngine = null;
+    protected Wrapper                 wrapEngine = null;
+
+    private int                       ivSize;
+    private byte[]                    iv;
 
     protected WrapCipherSpi()
     {
@@ -74,7 +81,15 @@
     protected WrapCipherSpi(
         Wrapper wrapEngine)
     {
+        this(wrapEngine, 0);
+    }
+
+    protected WrapCipherSpi(
+        Wrapper wrapEngine,
+        int     ivSize)
+    {
         this.wrapEngine = wrapEngine;
+        this.ivSize = ivSize;
     }
 
     protected int engineGetBlockSize()
@@ -84,7 +99,7 @@
 
     protected byte[] engineGetIV()
     {
-        return null;
+        return (byte[])iv.clone();
     }
 
     protected int engineGetKeySize(
@@ -152,8 +167,14 @@
         if (params instanceof javax.crypto.spec.IvParameterSpec)
         {
             IvParameterSpec iv = (IvParameterSpec) params;
-            CipherParameters paramPlusIV = new ParametersWithIV(param, iv.getIV());
-            param = paramPlusIV;
+            param = new ParametersWithIV(param, iv.getIV());
+        }
+
+        if (param instanceof KeyParameter && ivSize != 0)
+        {
+            iv = new byte[ivSize];
+            random.nextBytes(iv);
+            param = new ParametersWithIV(param, iv);
         }
 
         switch (opmode)
@@ -192,7 +213,7 @@
                 }
                 catch (Exception e)
                 {
-                    continue;
+                    // try next spec
                 }
             }
 
@@ -295,12 +316,14 @@
         byte[]  wrappedKey,
         String  wrappedKeyAlgorithm,
         int     wrappedKeyType)
+    // BEGIN android-removed
+    // throws InvalidKeyException
+    // END android-removed
+    // BEGIN android-added
     throws InvalidKeyException, NoSuchAlgorithmException
+    // END android-added
     {
-        // BEGIN android-note
-        // added ShortBufferException to throws statement
-        // END android-note
-        byte[] encoded = null;
+        byte[] encoded;
         try
         {
             if (wrapEngine == null)
@@ -336,7 +359,7 @@
              * the encrypted data.
              */
             ASN1InputStream bIn = new ASN1InputStream(encoded);
-            PrivateKey      privKey = null;
+            PrivateKey      privKey;
 
             try
             {
@@ -419,14 +442,8 @@
     //
     // classes that inherit directly from us
     //
-    public static class AESWrap
-        extends WrapCipherSpi
-    {
-        public AESWrap()
-        {
-            super(new AESWrapEngine());
-        }
-    }
+
+
 
     public static class DESEDEWrap
         extends WrapCipherSpi
@@ -437,14 +454,23 @@
         }
     }
 
-// BEGIN android-removed
-//    public static class RC2Wrap
-//        extends WrapCipherSpi
-//    {
-//        public RC2Wrap()
-//        {
-//            super(new RC2WrapEngine());
-//        }
-//    }
-// END android-removed
+    // BEGIN android-removed
+    // public static class RC2Wrap
+    //     extends WrapCipherSpi
+    // {
+    //     public RC2Wrap()
+    //     {
+    //         super(new RC2WrapEngine());
+    //     }
+    // }
+    //
+    // public static class RFC3211DESedeWrap
+    //     extends WrapCipherSpi
+    // {
+    //     public RFC3211DESedeWrap()
+    //     {
+    //         super(new RFC3211WrapEngine(new DESedeEngine()), 8);
+    //     }
+    // }
+    // END android-removed
 }
diff --git a/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
index ebf37fb..b86833a 100644
--- a/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
+++ b/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -1,5 +1,21 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.TBSCertList;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.x509.extension.X509ExtensionUtil;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
@@ -10,17 +26,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.TBSCertList;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.x509.extension.X509ExtensionUtil;
-
 /**
  * The following extensions are listed in RFC 2459 as relevant to CRL Entries
  * 
@@ -31,13 +36,17 @@
 {
     private TBSCertList.CRLEntry c;
 
-    private boolean isIndirect = false;
+    private boolean isIndirect;
 
-    private X500Principal previousCertificateIssuer = null;
+    private X500Principal previousCertificateIssuer;
+    private X500Principal certificateIssuer;
+    private int           hashValue;
+    private boolean       isHashValueSet;
 
     public X509CRLEntryObject(TBSCertList.CRLEntry c)
     {
         this.c = c;
+        this.certificateIssuer = loadCertificateIssuer();
     }
 
     /**
@@ -65,6 +74,7 @@
         this.c = c;
         this.isIndirect = isIndirect;
         this.previousCertificateIssuer = previousCertificateIssuer;
+        this.certificateIssuer = loadCertificateIssuer();
     }
 
     /**
@@ -74,15 +84,11 @@
     public boolean hasUnsupportedCriticalExtension()
     {
         Set extns = getCriticalExtensionOIDs();
-        if (extns != null && !extns.isEmpty())
-        {
-            return true;
-        }
 
-        return false;
+        return extns != null && !extns.isEmpty();
     }
 
-    public X500Principal getCertificateIssuer()
+    private X500Principal loadCertificateIssuer()
     {
         if (!isIndirect)
         {
@@ -106,17 +112,19 @@
                     return new X500Principal(names[i].getName().getDERObject().getDEREncoded());
                 }
             }
-            throw new RuntimeException(
-                    "Cannot extract directory name from certificate issuer CRL entry extension");
+            return null;
         }
         catch (IOException e)
         {
-            throw new RuntimeException(
-                    "Cannot extract certificate issuer CRL entry extension "
-                            + e);
+            return null;
         }
     }
 
+    public X500Principal getCertificateIssuer()
+    {
+        return certificateIssuer;
+    }
+
     private Set getExtensionOIDs(boolean critical)
     {
         X509Extensions extensions = c.getExtensions();
@@ -177,17 +185,27 @@
         return null;
     }
 
+    /**
+     * Cache the hashCode value - calculating it with the standard method.
+     * @return  calculated hashCode.
+     */
+    public int hashCode()
+    {
+        if (!isHashValueSet)
+        {
+            hashValue = super.hashCode();
+            isHashValueSet = true;
+        }
+
+        return hashValue;
+    }
+
     public byte[] getEncoded()
         throws CRLException
     {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        DEROutputStream dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(c);
-
-            return bOut.toByteArray();
+            return c.getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
@@ -217,6 +235,7 @@
 
         buf.append("      userCertificate: ").append(this.getSerialNumber()).append(nl);
         buf.append("       revocationDate: ").append(this.getRevocationDate()).append(nl);
+        buf.append("       certificateIssuer: ").append(this.getCertificateIssuer()).append(nl);
 
         X509Extensions extensions = c.getExtensions();
 
@@ -231,7 +250,37 @@
                 {
                     DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
                     X509Extension ext = extensions.getExtension(oid);
-                    buf.append(ext);
+                    if (ext.getValue() != null)
+                    {
+                        byte[]                  octs = ext.getValue().getOctets();
+                        ASN1InputStream dIn = new ASN1InputStream(octs);
+                        buf.append("                       critical(").append(ext.isCritical()).append(") ");
+                        try
+                        {
+                            if (oid.equals(X509Extensions.ReasonCode))
+                            {
+                                buf.append(new CRLReason(DEREnumerated.getInstance(dIn.readObject()))).append(nl);
+                            }
+                            else if (oid.equals(X509Extensions.CertificateIssuer))
+                            {
+                                buf.append("Certificate issuer: ").append(new GeneralNames((ASN1Sequence)dIn.readObject())).append(nl);
+                            }
+                            else 
+                            {
+                                buf.append(oid.getId());
+                                buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+                            buf.append(oid.getId());
+                            buf.append(" value = ").append("*****").append(nl);
+                        }
+                    }
+                    else
+                    {
+                        buf.append(nl);
+                    }
                 }
             }
         }
diff --git a/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
index 7533947..b196f3d 100644
--- a/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
+++ b/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
@@ -1,5 +1,25 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.TBSCertList;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.x509.extension.X509ExtensionUtil;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
@@ -18,21 +38,9 @@
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1OutputStream;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.x509.CertificateList;
-import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
-import org.bouncycastle.asn1.x509.TBSCertList;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.x509.extension.X509ExtensionUtil;
+import java.util.Collections;
 
 /**
  * The following extensions are listed in RFC 2459 as relevant to CRLs
@@ -49,6 +57,7 @@
     private CertificateList c;
     private String sigAlgName;
     private byte[] sigAlgParams;
+    private boolean isIndirect;
 
     public X509CRLObject(
         CertificateList c)
@@ -68,6 +77,8 @@
             {
                 this.sigAlgParams = null;
             }
+
+            this.isIndirect = isIndirectCRL();
         }
         catch (Exception e)
         {
@@ -82,34 +93,42 @@
     public boolean hasUnsupportedCriticalExtension()
     {
         Set extns = getCriticalExtensionOIDs();
-        if (extns != null && !extns.isEmpty())
+
+        if (extns == null)
         {
-            return true;
+            return false;
         }
 
-        return false;
+        extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+        extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+
+        return !extns.isEmpty();
     }
 
     private Set getExtensionOIDs(boolean critical)
     {
         if (this.getVersion() == 2)
         {
-            Set             set = new HashSet();
-            X509Extensions  extensions = c.getTBSCertList().getExtensions();
-            Enumeration     e = extensions.oids();
+            X509Extensions extensions = c.getTBSCertList().getExtensions();
 
-            while (e.hasMoreElements())
+            if (extensions != null)
             {
-                DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
-                X509Extension       ext = extensions.getExtension(oid);
+                Set set = new HashSet();
+                Enumeration e = extensions.oids();
 
-                if (critical == ext.isCritical())
+                while (e.hasMoreElements())
                 {
-                    set.add(oid.getId());
-                }
-            }
+                    DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+                    X509Extension ext = extensions.getExtension(oid);
 
-            return set;
+                    if (critical == ext.isCritical())
+                    {
+                        set.add(oid.getId());
+                    }
+                }
+
+                return set;
+            }
         }
 
         return null;
@@ -135,18 +154,13 @@
 
             if (ext != null)
             {
-                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-                DEROutputStream dOut = new DEROutputStream(bOut);
-
                 try
                 {
-                    dOut.writeObject(ext.getValue());
-
-                    return bOut.toByteArray();
+                    return ext.getValue().getEncoded();
                 }
                 catch (Exception e)
                 {
-                    throw new RuntimeException("error encoding " + e.toString());
+                    throw new IllegalStateException("error parsing " + e.toString());
                 }
             }
         }
@@ -157,14 +171,9 @@
     public byte[] getEncoded()
         throws CRLException
     {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-        DEROutputStream            dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(c);
-
-            return bOut.toByteArray();
+            return c.getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
@@ -185,7 +194,7 @@
     {
         if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
         {
-            throw new CRLException("Signature algorithm on CertifcateList does not match TBSCertList.");
+            throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
         }
 
         Signature sig = Signature.getInstance(getSigAlgName(), sigProvider);
@@ -239,84 +248,63 @@
 
         return null;
     }
+ 
+    private Set loadCRLEntries()
+    {
+        Set entrySet = new HashSet();
+        Enumeration certs = c.getRevokedCertificateEnumeration();
+
+        X500Principal previousCertificateIssuer = getIssuerX500Principal();
+        while (certs.hasMoreElements())
+        {
+            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+            X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+            entrySet.add(crlEntry);
+            previousCertificateIssuer = crlEntry.getCertificateIssuer();
+        }
+
+        return entrySet;
+    }
 
     public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
     {
-        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
-        boolean isIndirect = isIndirectCRL();
-        if (certs != null)
+        Enumeration certs = c.getRevokedCertificateEnumeration();
+
+        X500Principal previousCertificateIssuer = getIssuerX500Principal();
+        while (certs.hasMoreElements())
         {
-            X500Principal previousCertificateIssuer = getIssuerX500Principal();
-            for (int i = 0; i < certs.length; i++)
+            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+            X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+
+            if (serialNumber.equals(entry.getUserCertificate().getValue()))
             {
-                X509CRLEntryObject crlentry = new X509CRLEntryObject(certs[i],
-                        isIndirect, previousCertificateIssuer);
-                previousCertificateIssuer = crlentry.getCertificateIssuer();
-                if (crlentry.getSerialNumber().equals(serialNumber))
-                {
-                    return crlentry;
-                }
+                return crlEntry;
             }
+
+            previousCertificateIssuer = crlEntry.getCertificateIssuer();
         }
 
         return null;
     }
 
-    private boolean isIndirectCRL()
-    {
-        byte[] idp = getExtensionValue(X509Extensions.IssuingDistributionPoint.getId());
-        boolean isIndirect = false;
-        try
-        {
-            if (idp != null)
-            {
-                isIndirect = IssuingDistributionPoint.getInstance(
-                        X509ExtensionUtil.fromExtensionValue(idp))
-                        .isIndirectCRL();
-            }
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException(
-                    "Exception reading IssuingDistributionPoint" + e);
-        }
-
-        return isIndirect;
-    }
-  
     public Set getRevokedCertificates()
     {
-        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
-        boolean isIndirect = isIndirectCRL();
-        if (certs != null)
-        {
-            Set set = new HashSet();
-            X500Principal previousCertificateIssuer = getIssuerX500Principal();
-            for (int i = 0; i < certs.length; i++)
-            {
-                X509CRLEntryObject crlentry = new X509CRLEntryObject(certs[i],
-                        isIndirect, previousCertificateIssuer);
-                set.add(crlentry);
-                previousCertificateIssuer = crlentry.getCertificateIssuer();
-            }
+        Set entrySet = loadCRLEntries();
 
-            return set;
+        if (!entrySet.isEmpty())
+        {
+            return Collections.unmodifiableSet(entrySet);
         }
 
         return null;
     }
-  
+
     public byte[] getTBSCertList()
         throws CRLException
     {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-        DEROutputStream            dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(c.getTBSCertList());
-
-            return bOut.toByteArray();
+            return c.getTBSCertList().getEncoded("DER");
         }
         catch (IOException e)
         {
@@ -360,7 +348,128 @@
      */
     public String toString()
     {
-        return "X.509 CRL";
+        StringBuffer buf = new StringBuffer();
+        String nl = System.getProperty("line.separator");
+
+        buf.append("              Version: ").append(this.getVersion()).append(
+            nl);
+        buf.append("             IssuerDN: ").append(this.getIssuerDN())
+            .append(nl);
+        buf.append("          This update: ").append(this.getThisUpdate())
+            .append(nl);
+        buf.append("          Next update: ").append(this.getNextUpdate())
+            .append(nl);
+        buf.append("  Signature Algorithm: ").append(this.getSigAlgName())
+            .append(nl);
+
+        byte[] sig = this.getSignature();
+
+        buf.append("            Signature: ").append(
+            new String(Hex.encode(sig, 0, 20))).append(nl);
+        for (int i = 20; i < sig.length; i += 20)
+        {
+            if (i < sig.length - 20)
+            {
+                buf.append("                       ").append(
+                    new String(Hex.encode(sig, i, 20))).append(nl);
+            }
+            else
+            {
+                buf.append("                       ").append(
+                    new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+            }
+        }
+
+        X509Extensions extensions = c.getTBSCertList().getExtensions();
+
+        if (extensions != null)
+        {
+            Enumeration e = extensions.oids();
+
+            if (e.hasMoreElements())
+            {
+                buf.append("           Extensions: ").append(nl);
+            }
+
+            while (e.hasMoreElements())
+            {
+                DERObjectIdentifier oid = (DERObjectIdentifier) e.nextElement();
+                X509Extension ext = extensions.getExtension(oid);
+
+                if (ext.getValue() != null)
+                {
+                    byte[] octs = ext.getValue().getOctets();
+                    ASN1InputStream dIn = new ASN1InputStream(octs);
+                    buf.append("                       critical(").append(
+                        ext.isCritical()).append(") ");
+                    try
+                    {
+                        if (oid.equals(X509Extensions.CRLNumber))
+                        {
+                            buf.append(
+                                new CRLNumber(DERInteger.getInstance(
+                                    dIn.readObject()).getPositiveValue()))
+                                .append(nl);
+                        }
+                        else if (oid.equals(X509Extensions.DeltaCRLIndicator))
+                        {
+                            buf.append(
+                                "Base CRL: "
+                                    + new CRLNumber(DERInteger.getInstance(
+                                        dIn.readObject()).getPositiveValue()))
+                                .append(nl);
+                        }
+                        else if (oid
+                            .equals(X509Extensions.IssuingDistributionPoint))
+                        {
+                            buf.append(
+                                new IssuingDistributionPoint((ASN1Sequence) dIn
+                                    .readObject())).append(nl);
+                        }
+                        else if (oid
+                            .equals(X509Extensions.CRLDistributionPoints))
+                        {
+                            buf.append(
+                                new CRLDistPoint((ASN1Sequence) dIn
+                                    .readObject())).append(nl);
+                        }
+                        else if (oid.equals(X509Extensions.FreshestCRL))
+                        {
+                            buf.append(
+                                new CRLDistPoint((ASN1Sequence) dIn
+                                    .readObject())).append(nl);
+                        }
+                        else
+                        {
+                            buf.append(oid.getId());
+                            buf.append(" value = ").append(
+                                ASN1Dump.dumpAsString(dIn.readObject()))
+                                .append(nl);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        buf.append(oid.getId());
+                        buf.append(" value = ").append("*****").append(nl);
+                    }
+                }
+                else
+                {
+                    buf.append(nl);
+                }
+            }
+        }
+        Set set = getRevokedCertificates();
+        if (set != null)
+        {
+            Iterator it = set.iterator();
+            while (it.hasNext())
+            {
+                buf.append(it.next());
+                buf.append(nl);
+            }
+        }
+        return buf.toString();
     }
 
     /**
@@ -394,5 +503,28 @@
 
         return false;
     }
+
+    private boolean isIndirectCRL()
+        throws CRLException
+    {
+        byte[] idp = getExtensionValue(X509Extensions.IssuingDistributionPoint.getId());
+        boolean isIndirect = false;
+        try
+        {
+            if (idp != null)
+            {
+                isIndirect = IssuingDistributionPoint.getInstance(
+                        X509ExtensionUtil.fromExtensionValue(idp))
+                        .isIndirectCRL();
+            }
+        }
+        catch (Exception e)
+        {
+            throw new ExtCRLException(
+                    "Exception reading IssuingDistributionPoint", e);
+        }
+
+        return isIndirect;
+    }
 }
 
diff --git a/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java b/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
index d17fd59..e81211e 100644
--- a/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
+++ b/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
@@ -1,5 +1,31 @@
 package org.bouncycastle.jce.provider;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.NetscapeCertType;
+import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
+import org.bouncycastle.asn1.misc.VerisignCzagExtension;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
@@ -24,54 +50,67 @@
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Set;
-import java.util.Vector;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OutputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERBoolean;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-// BEGIN android-added
-import org.bouncycastle.asn1.OrderedTable;
-// END android-added
-import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
-import org.bouncycastle.asn1.misc.NetscapeCertType;
-import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
-import org.bouncycastle.asn1.misc.VerisignCzagExtension;
-import org.bouncycastle.asn1.util.ASN1Dump;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.encoders.Hex;
 
 public class X509CertificateObject
     extends X509Certificate
     implements PKCS12BagAttributeCarrier
 {
     private X509CertificateStructure    c;
-    // BEGIN android-changed
-    private OrderedTable                pkcs12 = new OrderedTable();
-    // END android-changed
+    private BasicConstraints            basicConstraints;
+    private boolean[]                   keyUsage;
+    private boolean                     hashValueSet;
+    private int                         hashValue;
+
+    private PKCS12BagAttributeCarrier   attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
     public X509CertificateObject(
         X509CertificateStructure    c)
+        throws CertificateParsingException
     {
         this.c = c;
+
+        try
+        {
+            byte[]  bytes = this.getExtensionBytes("2.5.29.19");
+
+            if (bytes != null)
+            {
+                basicConstraints = BasicConstraints.getInstance(ASN1Object.fromByteArray(bytes));
+            }
+        }
+        catch (Exception e)
+        {
+            throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
+        }
+
+        try
+        {
+            byte[] bytes = this.getExtensionBytes("2.5.29.15");
+            if (bytes != null)
+            {
+                DERBitString    bits = DERBitString.getInstance(ASN1Object.fromByteArray(bytes));
+
+                bytes = bits.getBytes();
+                int length = (bytes.length * 8) - bits.getPadBits();
+
+                keyUsage = new boolean[(length < 9) ? 9 : length];
+
+                for (int i = 0; i != length; i++)
+                {
+                    keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+                }
+            }
+            else
+            {
+                keyUsage = null;
+            }
+        }
+        catch (Exception e)
+        {
+            throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+        }
     }
 
     public void checkValidity()
@@ -84,12 +123,12 @@
         Date    date)
         throws CertificateExpiredException, CertificateNotYetValidException
     {
-        if (date.after(this.getNotAfter()))
+        if (date.getTime() > this.getNotAfter().getTime())  // for other VM compatibility
         {
             throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
         }
 
-        if (date.before(this.getNotBefore()))
+        if (date.getTime() < this.getNotBefore().getTime())
         {
             throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
         }
@@ -162,14 +201,9 @@
     public byte[] getTBSCertificate()
         throws CertificateEncodingException
     {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
         try
         {
-            dOut.writeObject(c.getTBSCertificate());
-
-            return bOut.toByteArray();
+            return c.getTBSCertificate().getEncoded(ASN1Encodable.DER);
         }
         catch (IOException e)
         {
@@ -189,11 +223,15 @@
     public String getSigAlgName()
     {
         Provider    prov = Security.getProvider("BC");
-        String      algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
 
-        if (algName != null)
+        if (prov != null)
         {
-            return algName;
+            String      algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+
+            if (algName != null)
+            {
+                return algName;
+            }
         }
 
         Provider[] provs = Security.getProviders();
@@ -203,7 +241,7 @@
         //
         for (int i = 0; i != provs.length; i++)
         {
-            algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+            String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
             if (algName != null)
             {
                 return algName;
@@ -226,22 +264,9 @@
      */
     public byte[] getSigAlgParams()
     {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-
         if (c.getSignatureAlgorithm().getParameters() != null)
         {
-            try
-            {
-                DEROutputStream         dOut = new DEROutputStream(bOut);
-
-                dOut.writeObject(c.getSignatureAlgorithm().getParameters());
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException("exception getting sig parameters " + e);
-            }
-
-            return bOut.toByteArray();
+            return c.getSignatureAlgorithm().getParameters().getDERObject().getDEREncoded();
         }
         else
         {
@@ -291,42 +316,13 @@
 
     public boolean[] getKeyUsage()
     {
-        byte[]  bytes = this.getExtensionBytes("2.5.29.15");
-        int     length = 0;
-
-        if (bytes != null)
-        {
-            try
-            {
-                ASN1InputStream dIn = new ASN1InputStream(bytes);
-                DERBitString    bits = (DERBitString)dIn.readObject();
-
-                bytes = bits.getBytes();
-                length = (bytes.length * 8) - bits.getPadBits();
-            }
-            catch (Exception e)
-            {
-                throw new RuntimeException("error processing key usage extension");
-            }
-
-            boolean[]       keyUsage = new boolean[(length < 9) ? 9 : length];
-
-            for (int i = 0; i != length; i++)
-            {
-                keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
-            }
-
-            return keyUsage;
-        }
-
-        return null;
+        return keyUsage;
     }
 
     public List getExtendedKeyUsage() 
         throws CertificateParsingException
     {
         byte[]  bytes = this.getExtensionBytes("2.5.29.37");
-        int     length = 0;
 
         if (bytes != null)
         {
@@ -354,48 +350,22 @@
     
     public int getBasicConstraints()
     {
-        byte[]  bytes = this.getExtensionBytes("2.5.29.19");
-
-        if (bytes != null)
+        if (basicConstraints != null)
         {
-            try
+            if (basicConstraints.isCA())
             {
-                ASN1InputStream dIn = new ASN1InputStream(bytes);
-                ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
-
-                if (seq.size() == 2)
+                if (basicConstraints.getPathLenConstraint() == null)
                 {
-                    if (((DERBoolean)seq.getObjectAt(0)).isTrue())
-                    {
-                        return ((DERInteger)seq.getObjectAt(1)).getValue().intValue();
-                    }
-                    else
-                    {
-                        return -1;
-                    }
+                    return Integer.MAX_VALUE;
                 }
-                else if (seq.size() == 1)
+                else
                 {
-                    if (seq.getObjectAt(0) instanceof DERBoolean)
-                    {
-                        if (((DERBoolean)seq.getObjectAt(0)).isTrue())
-                        {
-                            return Integer.MAX_VALUE;
-                        }
-                        else
-                        {
-                            return -1;
-                        }
-                    }
-                    else
-                    {
-                        return -1;
-                    }
+                    return basicConstraints.getPathLenConstraint().intValue();
                 }
             }
-            catch (Exception e)
+            else
             {
-                throw new RuntimeException("error processing basic constraints extension");
+                return -1;
             }
         }
 
@@ -457,18 +427,13 @@
 
             if (ext != null)
             {
-                ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-                DEROutputStream            dOut = new DEROutputStream(bOut);
-                
                 try
                 {
-                    dOut.writeObject(ext.getValue());
-
-                    return bOut.toByteArray();
+                    return ext.getValue().getEncoded();
                 }
                 catch (Exception e)
                 {
-                    throw new RuntimeException("error encoding " + e.toString());
+                    throw new IllegalStateException("error parsing " + e.toString());
                 }
             }
         }
@@ -518,8 +483,19 @@
                 while (e.hasMoreElements())
                 {
                     DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
-                    if (oid.getId().equals("2.5.29.15")
-                       || oid.getId().equals("2.5.29.19"))
+                    String              oidId = oid.getId();
+
+                    if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE)
+                     || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES)
+                     || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS)
+                     || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY)
+                     || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS)
+                     || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT)
+                     || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR)
+                     || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS)
+                     || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS)
+                     || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME)
+                     || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS))
                     {
                         continue;
                     }
@@ -542,25 +518,26 @@
         return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo());
     }
 
-// BEGIN android-changed
-    private ByteArrayOutputStream encodedOut;
+    // BEGIN android-changed
+    private byte[] encoded;
+    // END android-changed
     public byte[] getEncoded()
-            throws CertificateEncodingException {
-        synchronized (this) {
-            if (encodedOut == null) {
-                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-                DEROutputStream dOut = new DEROutputStream(bOut);
-                try {
-                    dOut.writeObject(c);
-                    encodedOut = bOut;
-                } catch (IOException e) {
-                    throw new CertificateEncodingException(e.toString());
-                }
+        throws CertificateEncodingException
+    {
+        try
+        {
+            // BEGIN android-changed
+            if (encoded == null) {
+                encoded = c.getEncoded(ASN1Encodable.DER);
             }
+            return encoded;
+            // END android-changed
         }
-        return encodedOut.toByteArray();
+        catch (IOException e)
+        {
+            throw new CertificateEncodingException(e.toString());
+        }
     }
-// END android-changed
 
     public boolean equals(
         Object o)
@@ -590,33 +567,45 @@
         }
     }
     
-    public int hashCode()
+    public synchronized int hashCode()
     {
-        return c.hashCode();
+        if (!hashValueSet)
+        {
+            hashValue = calculateHashCode();
+            hashValueSet = true;
+        }
+
+        return hashValue;
     }
     
+    private int calculateHashCode()
+    {
+        try
+        {
+            return Arrays.hashCode(this.getEncoded());
+        }
+        catch (CertificateEncodingException e)
+        {
+            return 0;
+        }
+    }
+
     public void setBagAttribute(
         DERObjectIdentifier oid,
         DEREncodable        attribute)
     {
-        // BEGIN android-changed
-        pkcs12.add(oid, attribute);
-        // END android-changed
+        attrCarrier.setBagAttribute(oid, attribute);
     }
 
     public DEREncodable getBagAttribute(
         DERObjectIdentifier oid)
     {
-        // BEGIN android-changed
-        return (DEREncodable)pkcs12.get(oid);
-        // END android-changed
+        return attrCarrier.getBagAttribute(oid);
     }
 
     public Enumeration getBagAttributeKeys()
     {
-        // BEGIN android-changed
-        return pkcs12.getKeys();
-        // END android-changed
+        return attrCarrier.getBagAttributeKeys();
     }
 
     public String toString()
@@ -720,7 +709,7 @@
         throws CertificateException, NoSuchAlgorithmException,
         InvalidKeyException, NoSuchProviderException, SignatureException
     {
-        Signature   signature = null;
+        Signature   signature;
         String      sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
 
         try
@@ -751,7 +740,7 @@
         PublicKey key, 
         Signature signature) 
         throws CertificateException, NoSuchAlgorithmException, 
-            SignatureException, InvalidKeyException, CertificateEncodingException
+            SignatureException, InvalidKeyException
     {
         if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature()))
         {
@@ -759,7 +748,8 @@
         }
 
         DEREncodable params = c.getSignatureAlgorithm().getParameters();
-        
+
+        // TODO This should go after the initVerify?
         X509SignatureUtil.setSignatureParameters(signature, params);
 
         signature.initVerify(key);
diff --git a/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java b/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java
index 2a79524..f55383e 100644
--- a/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java
+++ b/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java
@@ -1,14 +1,5 @@
 package org.bouncycastle.jce.provider;
 
-import java.io.IOException;
-import java.security.AlgorithmParameters;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.spec.PSSParameterSpec;
-
 import org.bouncycastle.asn1.ASN1Null;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEREncodable;
@@ -18,15 +9,24 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.PSSParameterSpec;
 
 class X509SignatureUtil
 {
     // BEGIN android-changed
-    private static final ASN1Null       derNull = DERNull.THE_ONE;
+    private static final ASN1Null       derNull = DERNull.INSTANCE;
     // END android-changed
     
     static void setSignatureParameters(
@@ -68,12 +68,14 @@
         
         if (params != null && !derNull.equals(params))
         {
-            if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
-            {
-                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
-                
-                return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
-            }
+            // BEGIN android-removed
+            // if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            // {
+            //     RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+            //
+            //     return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
+            // }
+            // END android-removed
             if (sigAlgId.getObjectId().equals(X9ObjectIdentifiers.ecdsa_with_SHA2))
             {
                 ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params);
@@ -100,10 +102,12 @@
         {
             return "SHA1";
         }
-        else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
-        {
-            return "SHA224";
-        }
+        // BEGIN android-removed
+        // else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
+        // {
+        //     return "SHA224";
+        // }
+        // END android-removed
         else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
         {
             return "SHA256";
@@ -116,22 +120,24 @@
         {
             return "SHA512";
         }
-        else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
-        {
-            return "RIPEMD128";
-        }
-        else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
-        {
-            return "RIPEMD160";
-        }
-        else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
-        {
-            return "RIPEMD256";
-        }
-        else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
-        {
-            return "GOST3411";
-        }
+        // BEGIN android-removed
+        // else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD128";
+        // }
+        // else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD160";
+        // }
+        // else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD256";
+        // }
+        // else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
+        // {
+        //     return "GOST3411";
+        // }
+        // END android-removed
         else
         {
             return digestAlgOID.getId();            
diff --git a/src/main/java/org/bouncycastle/jce/provider/symmetric/AES.java b/src/main/java/org/bouncycastle/jce/provider/symmetric/AES.java
new file mode 100644
index 0000000..f2e84e4
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/symmetric/AES.java
@@ -0,0 +1,179 @@
+package org.bouncycastle.jce.provider.symmetric;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.AESWrapEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
+// END android-removed
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.jce.provider.JCEBlockCipher;
+import org.bouncycastle.jce.provider.JCEKeyGenerator;
+import org.bouncycastle.jce.provider.JDKAlgorithmParameterGenerator;
+import org.bouncycastle.jce.provider.JDKAlgorithmParameters;
+import org.bouncycastle.jce.provider.WrapCipherSpi;
+
+import javax.crypto.spec.IvParameterSpec;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+public final class AES
+{
+    private AES()
+    {
+    }
+    
+    public static class ECB
+        extends JCEBlockCipher
+    {
+        public ECB()
+        {
+            super(new AESFastEngine());
+        }
+    }
+
+    // BEGIN android-removed
+    // public static class CBC
+    //    extends JCEBlockCipher
+    // {
+    //     public CBC()
+    //     {
+    //         super(new CBCBlockCipher(new AESFastEngine()), 128);
+    //     }
+    // }
+    //
+    // static public class CFB
+    //     extends JCEBlockCipher
+    // {
+    //     public CFB()
+    //     {
+    //         super(new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 128)), 128);
+    //     }
+    // }
+    //
+    // static public class OFB
+    //     extends JCEBlockCipher
+    // {
+    //     public OFB()
+    //     {
+    //         super(new BufferedBlockCipher(new OFBBlockCipher(new AESFastEngine(), 128)), 128);
+    //     }
+    // }
+    // END android-removed
+
+    static public class Wrap
+        extends WrapCipherSpi
+    {
+        public Wrap()
+        {
+            super(new AESWrapEngine());
+        }
+    }
+
+    // BEGIN android-removed
+    // public static class RFC3211Wrap
+    //     extends WrapCipherSpi
+    // {
+    //     public RFC3211Wrap()
+    //     {
+    //         super(new RFC3211WrapEngine(new AESEngine()), 16);
+    //     }
+    // }
+    // END android-removed
+
+    public static class KeyGen
+        extends JCEKeyGenerator
+    {
+        public KeyGen()
+        {
+            this(192);
+        }
+
+        public KeyGen(int keySize)
+        {
+            super("AES", keySize, new CipherKeyGenerator());
+        }
+    }
+
+    // BEGIN android-removed
+    // public static class KeyGen128
+    //     extends KeyGen
+    // {
+    //     public KeyGen128()
+    //     {
+    //         super(128);
+    //     }
+    // }
+    //
+    // public static class KeyGen192
+    //     extends KeyGen
+    // {
+    //     public KeyGen192()
+    //     {
+    //         super(192);
+    //     }
+    // }
+    //
+    // public static class KeyGen256
+    //     extends KeyGen
+    // {
+    //     public KeyGen256()
+    //     {
+    //         super(256);
+    //     }
+    // }
+    //
+    // public static class AlgParamGen
+    //     extends JDKAlgorithmParameterGenerator
+    // {
+    //     protected void engineInit(
+    //         AlgorithmParameterSpec genParamSpec,
+    //         SecureRandom random)
+    //         throws InvalidAlgorithmParameterException
+    //     {
+    //         throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for AES parameter generation.");
+    //     }
+    //
+    //     protected AlgorithmParameters engineGenerateParameters()
+    //     {
+    //         byte[]  iv = new byte[16];
+    //
+    //         if (random == null)
+    //         {
+    //             random = new SecureRandom();
+    //         }
+    //
+    //         random.nextBytes(iv);
+    //
+    //         AlgorithmParameters params;
+    //
+    //         try
+    //         {
+    //             params = AlgorithmParameters.getInstance("AES", "BC");
+    //             params.init(new IvParameterSpec(iv));
+    //         }
+    //         catch (Exception e)
+    //         {
+    //             throw new RuntimeException(e.getMessage());
+    //         }
+    //
+    //         return params;
+    //     }
+    // }
+    // END android-removed
+
+    public static class AlgParams
+        extends JDKAlgorithmParameters.IVAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "AES IV";
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/symmetric/AESMappings.java b/src/main/java/org/bouncycastle/jce/provider/symmetric/AESMappings.java
new file mode 100644
index 0000000..b3294c3
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/symmetric/AESMappings.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.jce.provider.symmetric;
+
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+
+import java.util.HashMap;
+
+public class AESMappings
+    extends HashMap
+{
+    /**
+     * These three got introduced in some messages as a result of a typo in an
+     * early document. We don't produce anything using these OID values, but we'll
+     * read them.
+     */
+    private static final String wrongAES128 = "2.16.840.1.101.3.4.2";
+    private static final String wrongAES192 = "2.16.840.1.101.3.4.22";
+    private static final String wrongAES256 = "2.16.840.1.101.3.4.42";
+
+    public AESMappings()
+    {
+        put("AlgorithmParameters.AES", "org.bouncycastle.jce.provider.symmetric.AES$AlgParams");
+        put("Alg.Alias.AlgorithmParameters." + wrongAES128, "AES");
+        put("Alg.Alias.AlgorithmParameters." + wrongAES192, "AES");
+        put("Alg.Alias.AlgorithmParameters." + wrongAES256, "AES");
+        put("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
+        put("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
+        put("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
+
+        // BEGIN android-removed
+        // put("AlgorithmParameterGenerator.AES", "org.bouncycastle.jce.provider.symmetric.AES$AlgParamGen");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES128, "AES");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES192, "AES");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + wrongAES256, "AES");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
+        // put("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
+        // END android-removed
+
+        put("Cipher.AES", "org.bouncycastle.jce.provider.symmetric.AES$ECB");
+        put("Alg.Alias.Cipher." + wrongAES128, "AES");
+        put("Alg.Alias.Cipher." + wrongAES192, "AES");
+        put("Alg.Alias.Cipher." + wrongAES256, "AES");
+        // BEGIN android-changed
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_ECB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_ECB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_ECB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_CBC, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_OFB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_OFB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_OFB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_CFB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_CFB, "AES");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_CFB, "AES");
+        // END android-changed
+        put("Cipher.AESWRAP", "org.bouncycastle.jce.provider.symmetric.AES$Wrap");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_wrap, "AESWRAP");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_wrap, "AESWRAP");
+        put("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_wrap, "AESWRAP");
+        // BEGIN android-removed
+        // put("Cipher.AESRFC3211WRAP", "org.bouncycastle.jce.provider.symmetric.AES$RFC3211Wrap");
+        // END android-removed
+
+        put("KeyGenerator.AES", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen");
+        // BEGIN android-removed
+        // put("KeyGenerator.2.16.840.1.101.3.4.2", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator.2.16.840.1.101.3.4.22", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator.2.16.840.1.101.3.4.42", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_ECB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_OFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_CFB, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // put("KeyGenerator.AESWRAP", "org.bouncycastle.jce.provider.symmetric.AES$KeyGen");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes128_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen128");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes192_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen192");
+        // put("KeyGenerator." + NISTObjectIdentifiers.id_aes256_wrap, "org.bouncycastle.jce.provider.symmetric.AES$KeyGen256");
+        // END android-removed
+    }
+}
diff --git a/src/main/java/org/bouncycastle/jce/provider/util/NullDigest.java b/src/main/java/org/bouncycastle/jce/provider/util/NullDigest.java
new file mode 100644
index 0000000..820ad1b
--- /dev/null
+++ b/src/main/java/org/bouncycastle/jce/provider/util/NullDigest.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.jce.provider.util;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+
+public class NullDigest
+    implements Digest
+{
+    private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+    public String getAlgorithmName()
+    {
+        return "NULL";
+    }
+
+    public int getDigestSize()
+    {
+        return bOut.size();
+    }
+
+    public void update(byte in)
+    {
+        bOut.write(in);
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        bOut.write(in, inOff, len);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        byte[] res = bOut.toByteArray();
+
+        System.arraycopy(res, 0, out, outOff, res.length);
+
+        reset();
+        
+        return res.length;
+    }
+
+    public void reset()
+    {
+        bOut.reset();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/bouncycastle/jce/spec/IEKeySpec.java b/src/main/java/org/bouncycastle/jce/spec/IEKeySpec.java
deleted file mode 100644
index 9859a22..0000000
--- a/src/main/java/org/bouncycastle/jce/spec/IEKeySpec.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.bouncycastle.jce.spec;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.KeySpec;
-
-import org.bouncycastle.jce.interfaces.IESKey;
-
-/**
- * key pair for use with an integrated encryptor - together
- * they provide what's required to generate the message.
- */
-public class IEKeySpec
-    implements KeySpec, IESKey
-{
-    private PublicKey   pubKey;
-    private PrivateKey  privKey;
-
-    /**
-     * @param privKey our private key.
-     * @param pubKey the public key of the sender/recipient.
-     */
-    public IEKeySpec(
-        PrivateKey  privKey,
-        PublicKey   pubKey)
-    {
-        this.privKey = privKey;
-        this.pubKey = pubKey;
-    }
-
-    /**
-     * return the intended recipient's/sender's public key.
-     */
-    public PublicKey getPublic()
-    {
-        return pubKey;
-    }
-
-    /**
-     * return the local private key.
-     */
-    public PrivateKey getPrivate()
-    {
-        return privKey;
-    }
-
-    /**
-     * return "IES"
-     */
-    public String getAlgorithm()
-    {
-        return "IES";
-    }
-
-    /**
-     * return null
-     */
-    public String getFormat()
-    {
-        return null;
-    }
-
-    /**
-     * returns null
-     */
-    public byte[] getEncoded()
-    {
-        return null;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/jce/spec/IESParameterSpec.java b/src/main/java/org/bouncycastle/jce/spec/IESParameterSpec.java
deleted file mode 100644
index 97c7d3a..0000000
--- a/src/main/java/org/bouncycastle/jce/spec/IESParameterSpec.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.bouncycastle.jce.spec;
-
-import java.security.spec.AlgorithmParameterSpec;
-
-/**
- * Parameter spec for an integrated encryptor, as in IEEE P1363a
- */
-public class IESParameterSpec
-    implements AlgorithmParameterSpec
-{
-    private byte[]  derivation;
-    private byte[]  encoding;
-    private int     macKeySize;
-
-    public IESParameterSpec(
-        byte[]  derivation,
-        byte[]  encoding,
-        int     macKeySize)
-    {
-        this.derivation = new byte[derivation.length];
-        System.arraycopy(derivation, 0, this.derivation, 0, derivation.length);
-
-        this.encoding = new byte[encoding.length];
-        System.arraycopy(encoding, 0, this.encoding, 0, encoding.length);
-
-        this.macKeySize = macKeySize;           
-    }
-
-    /**
-     * return the derivation vector.
-     */
-    public byte[] getDerivationV()
-    {
-        return derivation;
-    }
-
-    /**
-     * return the encoding vector.
-     */
-    public byte[] getEncodingV()
-    {
-        return encoding;
-    }
-
-    /**
-     * return the key size in bits for the MAC used with the message
-     */
-    public int getMacKeySize()
-    {
-        return macKeySize;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/openssl/EncryptionException.java b/src/main/java/org/bouncycastle/openssl/EncryptionException.java
new file mode 100644
index 0000000..b839568
--- /dev/null
+++ b/src/main/java/org/bouncycastle/openssl/EncryptionException.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.openssl;
+
+import java.io.IOException;
+
+public class EncryptionException
+    extends IOException
+{
+    private Throwable cause;
+
+    public EncryptionException(String msg)
+    {
+        super(msg);
+    }
+
+    public EncryptionException(String msg, Throwable ex)
+    {
+        super(msg);
+        this.cause = ex;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/bouncycastle/openssl/PEMUtilities.java b/src/main/java/org/bouncycastle/openssl/PEMUtilities.java
new file mode 100644
index 0000000..eaed72e
--- /dev/null
+++ b/src/main/java/org/bouncycastle/openssl/PEMUtilities.java
@@ -0,0 +1,183 @@
+package org.bouncycastle.openssl;
+
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+import java.io.IOException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+final class PEMUtilities
+{
+    static byte[] crypt(
+        boolean encrypt,
+        String  provider,
+        byte[]  bytes,
+        char[]  password,
+        String  dekAlgName,
+        byte[]  iv)
+        throws IOException
+    {
+        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
+        String                 alg;
+        String                 blockMode = "CBC";
+        String                 padding = "PKCS5Padding";
+        Key                    sKey;
+
+
+        // Figure out block mode and padding.
+        if (dekAlgName.endsWith("-CFB"))
+        {
+            blockMode = "CFB";
+            padding = "NoPadding";
+        }
+        if (dekAlgName.endsWith("-ECB") ||
+            "DES-EDE".equals(dekAlgName) ||
+            "DES-EDE3".equals(dekAlgName))
+        {
+            // ECB is actually the default (though seldom used) when OpenSSL
+            // uses DES-EDE (des2) or DES-EDE3 (des3).
+            blockMode = "ECB";
+            paramSpec = null;
+        }
+        if (dekAlgName.endsWith("-OFB"))
+        {
+            blockMode = "OFB";
+            padding = "NoPadding";
+        }
+
+
+        // Figure out algorithm and key size.
+        if (dekAlgName.startsWith("DES-EDE"))
+        {
+            alg = "DESede";
+            // "DES-EDE" is actually des2 in OpenSSL-speak!
+            // "DES-EDE3" is des3.
+            boolean des2 = !dekAlgName.startsWith("DES-EDE3");
+            sKey = getKey(password, alg, 24, iv, des2);
+        }
+        else if (dekAlgName.startsWith("DES-"))
+        {
+            alg = "DES";
+            sKey = getKey(password, alg, 8, iv);
+        }
+        else if (dekAlgName.startsWith("BF-"))
+        {
+            alg = "Blowfish";
+            sKey = getKey(password, alg, 16, iv);
+        }
+        else if (dekAlgName.startsWith("RC2-"))
+        {
+            alg = "RC2";
+            int keyBits = 128;
+            if (dekAlgName.startsWith("RC2-40-"))
+            {
+                keyBits = 40;
+            }
+            else if (dekAlgName.startsWith("RC2-64-"))
+            {
+                keyBits = 64;
+            }
+            sKey = getKey(password, alg, keyBits / 8, iv);
+            if (paramSpec == null) // ECB block mode
+            {
+                paramSpec = new RC2ParameterSpec(keyBits);
+            }
+            else
+            {
+                paramSpec = new RC2ParameterSpec(keyBits, iv);
+            }
+        }
+        else if (dekAlgName.startsWith("AES-"))
+        {
+            alg = "AES";
+            byte[] salt = iv;
+            if (salt.length > 8)
+            {
+                salt = new byte[8];
+                System.arraycopy(iv, 0, salt, 0, 8);
+            }
+
+            int keyBits;
+            if (dekAlgName.startsWith("AES-128-"))
+            {
+                keyBits = 128;
+            }
+            else if (dekAlgName.startsWith("AES-192-"))
+            {
+                keyBits = 192;
+            }
+            else if (dekAlgName.startsWith("AES-256-"))
+            {
+                keyBits = 256;
+            }
+            else
+            {
+                throw new EncryptionException("unknown AES encryption with private key");
+            }
+            sKey = getKey(password, "AES", keyBits / 8, salt);
+        }
+        else
+        {
+            throw new EncryptionException("unknown encryption with private key");
+        }
+
+        String transformation = alg + "/" + blockMode + "/" + padding;
+
+        try
+        {
+            Cipher c = Cipher.getInstance(transformation, provider);
+            int    mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+
+            if (paramSpec == null) // ECB block mode
+            {
+                c.init(mode, sKey);
+            }
+            else
+            {
+                c.init(mode, sKey, paramSpec);
+            }
+            return c.doFinal(bytes);
+        }
+        catch (Exception e)
+        {
+            throw new EncryptionException("exception using cipher - please check password and data.", e);
+        }
+    }
+
+    private static SecretKey getKey(
+        char[]  password,
+        String  algorithm,
+        int     keyLength,
+        byte[]  salt)
+    {
+        return getKey(password, algorithm, keyLength, salt, false);
+    }
+
+    private static SecretKey getKey(
+        char[]  password,
+        String  algorithm,
+        int     keyLength,
+        byte[]  salt,
+        boolean des2)
+    {
+        OpenSSLPBEParametersGenerator   pGen = new OpenSSLPBEParametersGenerator();
+
+        pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt);
+
+        KeyParameter keyParam;
+        keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8);
+        byte[] key = keyParam.getKey();
+        if (des2 && key.length >= 24)
+        {
+            // For DES2, we must copy first 8 bytes into the last 8 bytes.
+            System.arraycopy(key, 0, key, 16, 8);
+        }
+        return new javax.crypto.spec.SecretKeySpec(key, algorithm);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/openssl/PEMWriter.java b/src/main/java/org/bouncycastle/openssl/PEMWriter.java
index 5cbdf91..5c057c6 100644
--- a/src/main/java/org/bouncycastle/openssl/PEMWriter.java
+++ b/src/main/java/org/bouncycastle/openssl/PEMWriter.java
@@ -1,8 +1,6 @@
 package org.bouncycastle.openssl;
 
 import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.Writer;
 import java.math.BigInteger;
@@ -15,18 +13,13 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
 import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.RSAPrivateCrtKey;
 import java.security.interfaces.RSAPrivateKey;
 
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
 import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERSequence;
@@ -34,10 +27,8 @@
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
 import org.bouncycastle.asn1.x509.DSAParameter;
-import org.bouncycastle.crypto.PBEParametersGenerator;
-import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
-import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.jce.PKCS10CertificationRequest;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.x509.X509AttributeCertificate;
@@ -49,6 +40,7 @@
 public class PEMWriter
     extends BufferedWriter
 {
+    private String provider;
 
     /**
      * Base constructor.
@@ -57,9 +49,18 @@
      */
     public PEMWriter(Writer out)
     {
-        super(out);
+        this(out, "BC");
     }
-    
+
+    public PEMWriter(
+        Writer  out,
+        String  provider)
+    {
+        super(out);
+
+        this.provider = provider;
+    }
+
     private void writeHexEncoded(byte[] bytes)
         throws IOException
     {
@@ -134,18 +135,14 @@
         }
         else if (o instanceof PrivateKey)
         {
-            ByteArrayInputStream    bIn = new ByteArrayInputStream(((Key)o).getEncoded());
-            ASN1InputStream         aIn = new ASN1InputStream(bIn);
-            
-            PrivateKeyInfo          info = new PrivateKeyInfo((ASN1Sequence)aIn.readObject());
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
-            
+            PrivateKeyInfo info = new PrivateKeyInfo(
+                (ASN1Sequence) ASN1Object.fromByteArray(((Key)o).getEncoded()));
+
             if (o instanceof RSAPrivateKey)
             {
                 type = "RSA PRIVATE KEY";
-                
-                aOut.writeObject(info.getPrivateKey());
+
+                encoding = info.getPrivateKey().getEncoded();
             }
             else if (o instanceof DSAPrivateKey)
             {
@@ -164,15 +161,19 @@
                 
                 v.add(new DERInteger(y));
                 v.add(new DERInteger(x));
-                
-                aOut.writeObject(new DERSequence(v));
+
+                encoding = new DERSequence(v).getEncoded();
+            }
+            else if (((PrivateKey)o).getAlgorithm().equals("ECDSA"))
+            {
+                type = "EC PRIVATE KEY";
+
+                encoding = info.getPrivateKey().getEncoded();
             }
             else
             {
                 throw new IOException("Cannot identify private key");
             }
-            
-            encoding = bOut.toByteArray();
         }
         else if (o instanceof PublicKey)
         {
@@ -199,50 +200,33 @@
         {
             throw new IOException("unknown object passed - can't encode.");
         }
-        
-        this.write("-----BEGIN " + type + "-----");
-        this.newLine();
-        
+
+        writeHeader(type);
         writeEncoded(encoding);
-        
-        this.write("-----END " + type + "-----");
-        this.newLine();
+        writeFooter(type);
     }
-    
+
     public void writeObject(
-        Object       o,
+        Object       obj,
         String       algorithm,
         char[]       password,
         SecureRandom random)
         throws IOException
     {
-        byte[] salt = new byte[8];
-        
-        random.nextBytes(salt);
-       
-        OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
-        
-        pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt);
-        
-        SecretKey secretKey = null;
-        
-        if (algorithm.equalsIgnoreCase("DESEDE"))
+        if (obj instanceof KeyPair)
         {
-            // generate key
-            int keyLength = 24;
+            writeObject(((KeyPair)obj).getPrivate());
+            return;
+        }
 
-            secretKey = new SecretKeySpec(((KeyParameter)pGen.generateDerivedParameters(keyLength * 8)).getKey(), algorithm);
-        }
-        else
-        {
-            throw new IOException("unknown algorithm in writeObject");
-        }
-        
+        String type = null;
         byte[] keyData = null;
-        
-        if (o instanceof RSAPrivateCrtKey)
+
+        if (obj instanceof RSAPrivateCrtKey)
         {
-            RSAPrivateCrtKey k = (RSAPrivateCrtKey)o;
+            type = "RSA PRIVATE KEY";
+
+            RSAPrivateCrtKey k = (RSAPrivateCrtKey)obj;
 
             RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(
                 k.getModulus(),
@@ -253,43 +237,88 @@
                 k.getPrimeExponentP(),
                 k.getPrimeExponentQ(),
                 k.getCrtCoefficient());
-       
+
             // convert to bytearray
-            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-            ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
-            
-            aOut.writeObject(keyStruct);
-            aOut.close();
-            
-            keyData = bOut.toByteArray();
+            keyData = keyStruct.getEncoded();
         }
-       
-        byte[]  encData = null;
-        
-        // cipher  
-        try
+        else if (obj instanceof DSAPrivateKey)
         {
-            Cipher  c = Cipher.getInstance("DESede/CBC/PKCS5Padding", "BC");
-            c.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(salt));
-        
-            encData = c.doFinal(keyData);
+            type = "DSA PRIVATE KEY";
+
+            DSAPrivateKey       k = (DSAPrivateKey)obj;
+            DSAParams           p = k.getParams();
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            v.add(new DERInteger(0));
+            v.add(new DERInteger(p.getP()));
+            v.add(new DERInteger(p.getQ()));
+            v.add(new DERInteger(p.getG()));
+
+            BigInteger x = k.getX();
+            BigInteger y = p.getG().modPow(x, p.getP());
+
+            v.add(new DERInteger(y));
+            v.add(new DERInteger(x));
+
+            keyData = new DERSequence(v).getEncoded();
         }
-        catch (Exception e)
+        else if (obj instanceof PrivateKey && "ECDSA".equals(((PrivateKey)obj).getAlgorithm()))
         {
-            throw new IOException("exception using cipher: " + e.toString());
+            type = "EC PRIVATE KEY";
+
+            PrivateKeyInfo      privInfo = PrivateKeyInfo.getInstance(ASN1Object.fromByteArray(((PrivateKey)obj).getEncoded()));
+
+            keyData = privInfo.getPrivateKey().getEncoded();
         }
-       
+
+        if (type == null || keyData == null)
+        {
+            // TODO Support other types?
+            throw new IllegalArgumentException("Object type not supported: " + obj.getClass().getName());
+        }
+
+
+        String dekAlgName = Strings.toUpperCase(algorithm);
+
+        // Note: For backward compatibility
+        if (dekAlgName.equals("DESEDE"))
+        {
+            dekAlgName = "DES-EDE3-CBC";
+        }
+
+        int ivLength = dekAlgName.startsWith("AES-") ? 16 : 8;
+
+        byte[] iv = new byte[ivLength];
+        random.nextBytes(iv);
+
+        byte[] encData = PEMUtilities.crypt(true, provider, keyData, password, dekAlgName, iv);
+
+
         // write the data
-        this.write("-----BEGIN RSA PRIVATE KEY-----");
-        this.newLine();
+        writeHeader(type);
         this.write("Proc-Type: 4,ENCRYPTED");
         this.newLine();
-        this.write("DEK-Info: DES-EDE3-CBC,");
-        this.writeHexEncoded(salt);
+        this.write("DEK-Info: " + dekAlgName + ",");
+        this.writeHexEncoded(iv);
         this.newLine();
         this.newLine();
-        
         this.writeEncoded(encData);
-        this.write("-----END RSA PRIVATE KEY-----");   
+        writeFooter(type);
+    }
+
+    private void writeHeader(
+        String type)
+        throws IOException
+    {
+        this.write("-----BEGIN " + type + "-----");
+        this.newLine();
+    }
+
+    private void writeFooter(
+        String type)
+        throws IOException
+    {
+        this.write("-----END " + type + "-----");
+        this.newLine();
     }
 }
diff --git a/src/main/java/org/bouncycastle/util/Arrays.java b/src/main/java/org/bouncycastle/util/Arrays.java
index c6566d3..9600fd5 100644
--- a/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/src/main/java/org/bouncycastle/util/Arrays.java
@@ -11,14 +11,19 @@
     }
     
     public static boolean areEqual(
-        byte[]  a,
-        byte[]  b)
+        boolean[]  a,
+        boolean[]  b)
     {
         if (a == b)
         {
             return true;
         }
-        
+
+        if (a == null || b == null)
+        {
+            return false;
+        }
+
         if (a.length != b.length)
         {
             return false;
@@ -34,7 +39,104 @@
 
         return true;
     }
-    
+
+    public static boolean areEqual(
+        byte[]  a,
+        byte[]  b)
+    {
+        if (a == b)
+        {
+            return true;
+        }
+
+        if (a == null || b == null)
+        {
+            return false;
+        }
+
+        if (a.length != b.length)
+        {
+            return false;
+        }
+
+        for (int i = 0; i != a.length; i++)
+        {
+            if (a[i] != b[i])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * A constant time equals comparison - does not terminate early if
+     * test will fail.
+     *
+     * @param a first array
+     * @param b second array
+     * @return true if arrays equal, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(
+        byte[]  a,
+        byte[]  b)
+    {
+        if (a == b)
+        {
+            return true;
+        }
+
+        if (a == null || b == null)
+        {
+            return false;
+        }
+
+        if (a.length != b.length)
+        {
+            return false;
+        }
+
+        int nonEqual = 0;
+
+        for (int i = 0; i != a.length; i++)
+        {
+            nonEqual |= (a[i] ^ b[i]);
+        }
+
+        return nonEqual == 0;
+    }
+
+    public static boolean areEqual(
+        int[]  a,
+        int[]  b)
+    {
+        if (a == b)
+        {
+            return true;
+        }
+
+        if (a == null || b == null)
+        {
+            return false;
+        }
+
+        if (a.length != b.length)
+        {
+            return false;
+        }
+
+        for (int i = 0; i != a.length; i++)
+        {
+            if (a[i] != b[i])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     public static void fill(
         byte[] array,
         byte value)
@@ -64,4 +166,49 @@
             array[i] = value;
         }
     }
+
+    public static int hashCode(byte[] data)
+    {
+        if (data == null)
+        {
+            return 0;
+        }
+
+        int i = data.length;
+        int hc = i + 1;
+
+        while (--i >= 0)
+        {
+            hc *= 257;
+            hc ^= data[i];
+        }
+
+        return hc;
+    }
+
+    public static byte[] clone(byte[] data)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        byte[] copy = new byte[data.length];
+
+        System.arraycopy(data, 0, copy, 0, data.length);
+
+        return copy;
+    }
+
+    public static int[] clone(int[] data)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        int[] copy = new int[data.length];
+
+        System.arraycopy(data, 0, copy, 0, data.length);
+
+        return copy;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/util/BigIntegers.java b/src/main/java/org/bouncycastle/util/BigIntegers.java
index 3d1c456..2115799 100644
--- a/src/main/java/org/bouncycastle/util/BigIntegers.java
+++ b/src/main/java/org/bouncycastle/util/BigIntegers.java
@@ -1,12 +1,16 @@
 package org.bouncycastle.util;
 
 import java.math.BigInteger;
+import java.security.SecureRandom;
 
 /**
  * BigInteger utilities.
  */
 public final class BigIntegers
 {
+    private static final int MAX_ITERATIONS = 1000;
+    private static final BigInteger ZERO = BigInteger.valueOf(0);
+
     /**
      * Return the passed in value as an unsigned byte array.
      * 
@@ -29,4 +33,46 @@
         
         return bytes;
     }
+
+    /**
+     * Return a random BigInteger not less than 'min' and not greater than 'max'
+     * 
+     * @param min the least value that may be generated
+     * @param max the greatest value that may be generated
+     * @param random the source of randomness
+     * @return a random BigInteger value in the range [min,max]
+     */
+    public static BigInteger createRandomInRange(
+        BigInteger      min,
+        BigInteger      max,
+        SecureRandom    random)
+    {
+        int cmp = min.compareTo(max);
+        if (cmp >= 0)
+        {
+            if (cmp > 0)
+            {
+                throw new IllegalArgumentException("'min' may not be greater than 'max'");
+            }
+
+            return min;
+        }
+
+        if (min.bitLength() > max.bitLength() / 2)
+        {
+            return createRandomInRange(ZERO, max.subtract(min), random).add(min);
+        }
+
+        for (int i = 0; i < MAX_ITERATIONS; ++i)
+        {
+            BigInteger x = new BigInteger(max.bitLength(), random);
+            if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0)
+            {
+                return x;
+            }
+        }
+
+        // fall back to a faster (restricted) method
+        return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/util/IPAddress.java b/src/main/java/org/bouncycastle/util/IPAddress.java
new file mode 100644
index 0000000..9f5d1cb
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/IPAddress.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.util;
+
+public class IPAddress
+{
+    /**
+     * Validate the given IPv4 or IPv6 address.
+     *
+     * @param address the IP address as a String.
+     *
+     * @return true if a valid address, false otherwise
+     */
+    public static boolean isValid(
+        String address)
+    {
+        return isValidIPv4(address) || isValidIPv6(address);
+    }
+
+    /**
+     * Validate the given IPv4 or IPv6 address and netmask.
+     *
+     * @param address the IP address as a String.
+     *
+     * @return true if a valid address with netmask, false otherwise
+     */
+    public static boolean isValidWithNetMask(
+        String address)
+    {
+        return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
+    }
+
+    /**
+     * Validate the given IPv4 address.
+     * 
+     * @param address the IP address as a String.
+     *
+     * @return true if a valid IPv4 address, false otherwise
+     */
+    public static boolean isValidIPv4(
+        String address)
+    {
+        if (address.length() == 0)
+        {
+            return false;
+        }
+
+        int octet;
+        int octets = 0;
+        
+        String temp = address+".";
+
+        int pos;
+        int start = 0;
+        while (start < temp.length()
+            && (pos = temp.indexOf('.', start)) > start)
+        {
+            if (octets == 4)
+            {
+                return false;
+            }
+            try
+            {
+                octet = Integer.parseInt(temp.substring(start, pos));
+            }
+            catch (NumberFormatException ex)
+            {
+                return false;
+            }
+            if (octet < 0 || octet > 255)
+            {
+                return false;
+            }
+            start = pos + 1;
+            octets++;
+        }
+
+        return octets == 4;
+    }
+
+    public static boolean isValidIPv4WithNetmask(
+        String address)
+    {
+        int index = address.indexOf("/");
+        String mask = address.substring(index + 1);
+
+        return (index > 0) && isValidIPv4(address.substring(0, index))
+                           && (isValidIPv4(mask) || isMaskValue(mask, 32));
+    }
+
+    public static boolean isValidIPv6WithNetmask(
+        String address)
+    {
+        int index = address.indexOf("/");
+        String mask = address.substring(index + 1);
+
+        return (index > 0) && (isValidIPv6(address.substring(0, index))
+                           && (isValidIPv6(mask) || isMaskValue(mask, 128)));
+    }
+
+    private static boolean isMaskValue(String component, int size)
+    {
+        try
+        {
+            int value = Integer.parseInt(component);
+
+            return value >= 0 && value <= size;
+        }
+        catch (NumberFormatException e)
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Validate the given IPv6 address.
+     *
+     * @param address the IP address as a String.
+     *
+     * @return true if a valid IPv4 address, false otherwise
+     */
+    public static boolean isValidIPv6(
+        String address)
+    {
+        if (address.length() == 0)
+        {
+            return false;
+        }
+
+        int octet;
+        int octets = 0;
+
+        String temp = address + ":";
+        boolean doubleColonFound = false;
+        int pos;
+        int start = 0;
+        while (start < temp.length()
+            && (pos = temp.indexOf(':', start)) >= start)
+        {
+            if (octets == 8)
+            {
+                return false;
+            }
+
+            if (start != pos)
+            {
+                String value = temp.substring(start, pos);
+
+                if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+                {
+                    if (!isValidIPv4(value))
+                    {
+                        return false;
+                    }
+
+                    octets++; // add an extra one as address covers 2 words.
+                }
+                else
+                {
+                    try
+                    {
+                        octet = Integer.parseInt(temp.substring(start, pos), 16);
+                    }
+                    catch (NumberFormatException ex)
+                    {
+                        return false;
+                    }
+                    if (octet < 0 || octet > 0xffff)
+                    {
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+                {
+                    return false;
+                }
+                doubleColonFound = true;
+            }
+            start = pos + 1;
+            octets++;
+        }
+
+        return octets == 8 || doubleColonFound;
+    }
+}
+
+
diff --git a/src/main/java/org/bouncycastle/util/Selector.java b/src/main/java/org/bouncycastle/util/Selector.java
new file mode 100644
index 0000000..7ad86bf
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/Selector.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+public interface Selector
+    extends Cloneable
+{
+    boolean match(Object obj);
+
+    Object clone();
+}
diff --git a/src/main/java/org/bouncycastle/util/Store.java b/src/main/java/org/bouncycastle/util/Store.java
new file mode 100644
index 0000000..b994c92
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/Store.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+import java.util.Collection;
+
+public interface Store
+{
+    Collection getMatches(Selector selector)
+        throws StoreException;
+}
diff --git a/src/main/java/org/bouncycastle/util/StoreException.java b/src/main/java/org/bouncycastle/util/StoreException.java
new file mode 100644
index 0000000..5ea09e8
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/StoreException.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.util;
+
+public class StoreException
+    extends RuntimeException
+{
+    private Throwable _e;
+
+    public StoreException(String s, Throwable e)
+    {
+        super(s);
+        _e = e;
+    }
+
+    public Throwable getCause()
+    {
+        return _e;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/util/Strings.java b/src/main/java/org/bouncycastle/util/Strings.java
index ff42595..e69eade 100644
--- a/src/main/java/org/bouncycastle/util/Strings.java
+++ b/src/main/java/org/bouncycastle/util/Strings.java
@@ -1,7 +1,7 @@
 package org.bouncycastle.util;
 
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
+import java.util.Vector;
 
 public final class Strings
 {
@@ -44,7 +44,7 @@
 
             if ((bytes[i] & 0xf0) == 0xf0)
             {
-                int codePoint = ((bytes[i] & 0x0F) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F);
+                int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F);
                 int U = codePoint - 0x10000;
                 char W1 = (char)(0xD800 | (U >> 10));
                 char W2 = (char)(0xDC00 | (U & 0x3FF));
@@ -54,13 +54,18 @@
             }
             else if ((bytes[i] & 0xe0) == 0xe0)
             {
-                ch = (char)(((bytes[i] & 0x1f) << 12)
+                ch = (char)(((bytes[i] & 0x0f) << 12)
                         | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
                 i += 3;
             }
+            else if ((bytes[i] & 0xd0) == 0xd0)
+            {
+                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
+                i += 2;
+            }
             else if ((bytes[i] & 0xc0) == 0xc0)
             {
-                ch = (char)(((bytes[i] & 0x3f) << 6) | (bytes[i + 1] & 0x3f));
+                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
                 i += 2;
             }
             else
@@ -77,8 +82,13 @@
     
     public static byte[] toUTF8ByteArray(String string)
     {
+        return toUTF8ByteArray(string.toCharArray());
+    }
+
+    public static byte[] toUTF8ByteArray(char[] string)
+    {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        char[] c = string.toCharArray();
+        char[] c = string;
         int i = 0;
 
         while (i < c.length)
@@ -188,4 +198,49 @@
         
         return string;
     }
+
+    public static byte[] toByteArray(String string)
+    {
+        byte[] bytes = new byte[string.length()];
+
+        for (int i = 0; i != bytes.length; i++)
+        {
+            char ch = string.charAt(i);
+
+            bytes[i] = (byte)ch;
+        }
+
+        return bytes;
+    }
+
+    public static String[] split(String input, char delimiter)
+    {
+        Vector           v = new Vector();
+        boolean moreTokens = true;
+        String subString;
+
+        while (moreTokens)
+        {
+            int tokenLocation = input.indexOf(delimiter);
+            if (tokenLocation > 0)
+            {
+                subString = input.substring(0, tokenLocation);
+                v.addElement(subString);
+                input = input.substring(tokenLocation + 1);
+            }
+            else
+            {
+                moreTokens = false;
+                v.addElement(input);
+            }
+        }
+
+        String[] res = new String[v.size()];
+
+        for (int i = 0; i != res.length; i++)
+        {
+            res[i] = (String)v.elementAt(i);
+        }
+        return res;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/util/encoders/Base64.java b/src/main/java/org/bouncycastle/util/encoders/Base64.java
index 1dc94b5..93fed64 100644
--- a/src/main/java/org/bouncycastle/util/encoders/Base64.java
+++ b/src/main/java/org/bouncycastle/util/encoders/Base64.java
@@ -16,7 +16,8 @@
     public static byte[] encode(
         byte[]    data)
     {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+        int len = (data.length + 2) / 3 * 4;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
         
         try
         {
@@ -66,7 +67,8 @@
     public static byte[] decode(
         byte[]    data)
     {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+        int len = data.length / 4 * 3;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
         
         try
         {
@@ -88,7 +90,8 @@
     public static byte[] decode(
         String    data)
     {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+        int len = data.length() / 4 * 3;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
         
         try
         {
diff --git a/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java b/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java
deleted file mode 100644
index 672430a..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/BufferedDecoder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-
-/**
- * a buffering class to allow translation from one format to another to
- * be done in discrete chunks.
- */
-public class BufferedDecoder
-{
-    protected byte[]        buf;
-    protected int           bufOff;
-
-    protected Translator    translator;
-
-    /**
-     * @param translator the translator to use.
-     * @param bufSize amount of input to buffer for each chunk.
-     */
-    public BufferedDecoder(
-        Translator  translator,
-        int         bufSize)
-    {
-        this.translator = translator;
-
-        if ((bufSize % translator.getEncodedBlockSize()) != 0)
-        {
-            throw new IllegalArgumentException("buffer size not multiple of input block size");
-        }
-
-        buf = new byte[bufSize];
-        bufOff = 0;
-    }
-
-    public int processByte(
-        byte        in,
-        byte[]      out,
-        int         outOff)
-    {
-        int         resultLen = 0;
-
-        buf[bufOff++] = in;
-
-        if (bufOff == buf.length)
-        {
-            resultLen = translator.decode(buf, 0, buf.length, out, outOff);
-            bufOff = 0;
-        }
-
-        return resultLen;
-    }
-
-    public int processBytes(
-        byte[]      in,
-        int         inOff,
-        int         len,
-        byte[]      out,
-        int         outOff)
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        int resultLen = 0;
-        int gapLen = buf.length - bufOff;
-
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-
-            resultLen += translator.decode(buf, 0, buf.length, out, outOff);
-
-            bufOff = 0;
-
-            len -= gapLen;
-            inOff += gapLen;
-            outOff += resultLen;
-
-            int chunkSize = len - (len % buf.length);
-
-            resultLen += translator.decode(in, inOff, chunkSize, out, outOff);
-
-            len -= chunkSize;
-            inOff += chunkSize;
-        }
-
-        if (len != 0)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, len);
-
-            bufOff += len;
-        }
-
-        return resultLen;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java b/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java
deleted file mode 100644
index 107eee8..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/BufferedEncoder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-
-/**
- * a buffering class to allow translation from one format to another to
- * be done in discrete chunks.
- */
-public class BufferedEncoder
-{
-    protected byte[]        buf;
-    protected int           bufOff;
-
-    protected Translator    translator;
-
-    /**
-     * @param translator the translator to use.
-     * @param bufSize amount of input to buffer for each chunk.
-     */
-    public BufferedEncoder(
-        Translator  translator,
-        int         bufSize)
-    {
-        this.translator = translator;
-
-        if ((bufSize % translator.getEncodedBlockSize()) != 0)
-        {
-            throw new IllegalArgumentException("buffer size not multiple of input block size");
-        }
-
-        buf = new byte[bufSize];
-        bufOff = 0;
-    }
-
-    public int processByte(
-        byte        in,
-        byte[]      out,
-        int         outOff)
-    {
-        int         resultLen = 0;
-
-        buf[bufOff++] = in;
-
-        if (bufOff == buf.length)
-        {
-            resultLen = translator.encode(buf, 0, buf.length, out, outOff);
-            bufOff = 0;
-        }
-
-        return resultLen;
-    }
-
-    public int processBytes(
-        byte[]      in,
-        int         inOff,
-        int         len,
-        byte[]      out,
-        int         outOff)
-    {
-        if (len < 0)
-        {
-            throw new IllegalArgumentException("Can't have a negative input length!");
-        }
-
-        int resultLen = 0;
-        int gapLen = buf.length - bufOff;
-
-        if (len > gapLen)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, gapLen);
-
-            resultLen += translator.encode(buf, 0, buf.length, out, outOff);
-
-            bufOff = 0;
-
-            len -= gapLen;
-            inOff += gapLen;
-            outOff += resultLen;
-
-            int chunkSize = len - (len % buf.length);
-
-            resultLen += translator.encode(in, inOff, chunkSize, out, outOff);
-
-            len -= chunkSize;
-            inOff += chunkSize;
-        }
-
-        if (len != 0)
-        {
-            System.arraycopy(in, inOff, buf, bufOff, len);
-
-            bufOff += len;
-        }
-
-        return resultLen;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java b/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java
deleted file mode 100644
index 3fff65a..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/HexTranslator.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-/**
- * Converters for going from hex to binary and back. Note: this class assumes ASCII processing.
- */
-public class HexTranslator
-    implements Translator
-{
-    private static final byte[]   hexTable = 
-        { 
-            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
-            (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'
-        };
-
-    /**
-     * size of the output block on encoding produced by getDecodedBlockSize()
-     * bytes.
-     */
-    public int getEncodedBlockSize()
-    {
-        return 2;
-    }
-
-    public int encode(
-        byte[]  in,
-        int     inOff,
-        int     length,
-        byte[]  out,
-        int     outOff)
-    {
-        for (int i = 0, j = 0; i < length; i++, j += 2)
-        {
-            out[outOff + j] = hexTable[(in[inOff] >> 4) & 0x0f];
-            out[outOff + j + 1] = hexTable[in[inOff] & 0x0f];
-
-            inOff++;
-        }
-
-        return length * 2;
-    }
-
-    /**
-     * size of the output block on decoding produced by getEncodedBlockSize()
-     * bytes.
-     */
-    public int getDecodedBlockSize()
-    {
-        return 1;
-    }
-
-    public int decode(
-        byte[]  in,
-        int     inOff,
-        int     length,
-        byte[]  out,
-        int     outOff)
-    {
-        int halfLength = length / 2;
-        byte left, right;
-        for (int i = 0; i < halfLength; i++)
-        {
-            left  = in[inOff + i * 2];
-            right = in[inOff + i * 2 + 1];
-            
-            if (left < (byte)'a')
-            {
-                out[outOff] = (byte)((left - '0') << 4);
-            }
-            else
-            {
-                out[outOff] = (byte)((left - 'a' + 10) << 4);
-            }
-            if (right < (byte)'a')
-            {
-                out[outOff] += (byte)(right - '0');
-            }
-            else
-            {
-                out[outOff] += (byte)(right - 'a' + 10);
-            }
-
-            outOff++;
-        }
-
-        return halfLength;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/util/encoders/Translator.java b/src/main/java/org/bouncycastle/util/encoders/Translator.java
deleted file mode 100644
index a3a0cb8..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/Translator.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-/**
- * general interface for an translator.
- */
-public interface Translator
-{
-    /**
-     * size of the output block on encoding produced by getDecodedBlockSize()
-     * bytes.
-     */
-    public int getEncodedBlockSize();
-
-    public int encode(byte[] in, int inOff, int length, byte[] out, int outOff);
-
-    /**
-     * size of the output block on decoding produced by getEncodedBlockSize()
-     * bytes.
-     */
-    public int getDecodedBlockSize();
-
-    public int decode(byte[] in, int inOff, int length, byte[] out, int outOff);
-}
diff --git a/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java b/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java
deleted file mode 100644
index a22d94a..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/UrlBase64.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Convert binary data to and from UrlBase64 encoding.  This is identical to
- * Base64 encoding, except that the padding character is "." and the other 
- * non-alphanumeric characters are "-" and "_" instead of "+" and "/".
- * <p>
- * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
- * data that is safe for use as an URL parameter. Base64 encoding does not
- * produce encoded values that are safe for use in URLs, since "/" can be 
- * interpreted as a path delimiter; "+" is the encoded form of a space; and
- * "=" is used to separate a name from the corresponding value in an URL 
- * parameter.
- */
-public class UrlBase64
-{
-    private static final Encoder encoder = new UrlBase64Encoder();
-    
-    /**
-     * Encode the input data producing a URL safe base 64 encoded byte array.
-     *
-     * @return a byte array containing the URL safe base 64 encoded data.
-     */
-    public static byte[] encode(
-        byte[]    data)
-    {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-        
-        try
-        {
-            encoder.encode(data, 0, data.length, bOut);
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException("exception encoding URL safe base64 string: " + e);
-        }
-        
-        return bOut.toByteArray();
-    }
-
-    /**
-     * Encode the byte data writing it to the given output stream.
-     *
-     * @return the number of bytes produced.
-     */
-    public static int encode(
-        byte[]                data,
-        OutputStream    out)
-        throws IOException
-    {
-        return encoder.encode(data, 0, data.length, out);
-    }
-    
-    /**
-     * Decode the URL safe base 64 encoded input data - white space will be ignored.
-     *
-     * @return a byte array representing the decoded data.
-     */
-    public static byte[] decode(
-        byte[]    data)
-    {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-        
-        try
-        {
-            encoder.decode(data, 0, data.length, bOut);
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException("exception decoding URL safe base64 string: " + e);
-        }
-        
-        return bOut.toByteArray();
-    }
-    
-    /**
-     * decode the URL safe base 64 encoded byte data writing it to the given output stream,
-     * whitespace characters will be ignored.
-     *
-     * @return the number of bytes produced.
-     */
-    public static int decode(
-        byte[]                data,
-        OutputStream    out)
-        throws IOException
-    {
-        return encoder.decode(data, 0, data.length, out);
-    }
-    
-    /**
-     * decode the URL safe base 64 encoded String data - whitespace will be ignored.
-     *
-     * @return a byte array representing the decoded data.
-     */
-    public static byte[] decode(
-        String    data)
-    {
-        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-        
-        try
-        {
-            encoder.decode(data, bOut);
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException("exception decoding URL safe base64 string: " + e);
-        }
-        
-        return bOut.toByteArray();
-    }
-    
-    /**
-     * Decode the URL safe base 64 encoded String data writing it to the given output stream,
-     * whitespace characters will be ignored.
-     *
-     * @return the number of bytes produced.
-     */
-    public static int decode(
-        String                data,
-        OutputStream    out)
-        throws IOException
-    {
-        return encoder.decode(data, out);
-    }
-}
diff --git a/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java b/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java
deleted file mode 100644
index a5fff5e..0000000
--- a/src/main/java/org/bouncycastle/util/encoders/UrlBase64Encoder.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.bouncycastle.util.encoders;
-
-/**
- * Convert binary data to and from UrlBase64 encoding.  This is identical to
- * Base64 encoding, except that the padding character is "." and the other 
- * non-alphanumeric characters are "-" and "_" instead of "+" and "/".
- * <p>
- * The purpose of UrlBase64 encoding is to provide a compact encoding of binary
- * data that is safe for use as an URL parameter. Base64 encoding does not
- * produce encoded values that are safe for use in URLs, since "/" can be 
- * interpreted as a path delimiter; "+" is the encoded form of a space; and
- * "=" is used to separate a name from the corresponding value in an URL 
- * parameter.
- */
-public class UrlBase64Encoder extends Base64Encoder
-{
-    public UrlBase64Encoder()
-    {
-        encodingTable[encodingTable.length - 2] = (byte) '-';
-        encodingTable[encodingTable.length - 1] = (byte) '_';
-        padding = (byte) '.';
-        // we must re-create the decoding table with the new encoded values.
-        initialiseDecodingTable();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java b/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java
new file mode 100644
index 0000000..01af8da
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/io/StreamOverflowException.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+
+public class StreamOverflowException
+    extends IOException
+{
+    public StreamOverflowException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/util/io/Streams.java b/src/main/java/org/bouncycastle/util/io/Streams.java
new file mode 100644
index 0000000..41560b5
--- /dev/null
+++ b/src/main/java/org/bouncycastle/util/io/Streams.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.util.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public final class Streams
+{
+    private static int BUFFER_SIZE = 512;
+
+    public static void drain(InputStream inStr)
+        throws IOException
+    {
+        byte[] bs = new byte[BUFFER_SIZE];
+        while (inStr.read(bs, 0, bs.length) >= 0)
+        {
+        }
+    }
+
+    public static byte[] readAll(InputStream inStr)
+        throws IOException
+    {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        pipeAll(inStr, buf);
+        return buf.toByteArray();
+    }
+
+    public static byte[] readAllLimited(InputStream inStr, int limit)
+        throws IOException
+    {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        pipeAllLimited(inStr, limit, buf);
+        return buf.toByteArray();
+    }
+
+    public static int readFully(InputStream inStr, byte[] buf)
+        throws IOException
+    {
+        return readFully(inStr, buf, 0, buf.length);
+    }
+
+    public static int readFully(InputStream inStr, byte[] buf, int off, int len)
+        throws IOException
+    {
+        int totalRead = 0;
+        while (totalRead < len)
+        {
+            int numRead = inStr.read(buf, off + totalRead, len - totalRead);
+            if (numRead < 0)
+            {
+                break;
+            }
+            totalRead += numRead;
+        }
+        return totalRead;
+    }
+
+    public static void pipeAll(InputStream inStr, OutputStream outStr)
+        throws IOException
+    {
+        byte[] bs = new byte[BUFFER_SIZE];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            outStr.write(bs, 0, numRead);
+        }
+    }
+
+    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
+        throws IOException
+    {
+        long total = 0;
+        byte[] bs = new byte[BUFFER_SIZE];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            total += numRead;
+            if (total > limit)
+            {
+                throw new StreamOverflowException("Data Overflow");
+            }
+            outStr.write(bs, 0, numRead);
+        }
+        return total;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/AttributeCertificateHolder.java b/src/main/java/org/bouncycastle/x509/AttributeCertificateHolder.java
index d8aa122..48ef720 100644
--- a/src/main/java/org/bouncycastle/x509/AttributeCertificateHolder.java
+++ b/src/main/java/org/bouncycastle/x509/AttributeCertificateHolder.java
@@ -1,79 +1,79 @@
 package org.bouncycastle.x509;
 
-import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.Holder;
 import org.bouncycastle.asn1.x509.IssuerSerial;
+import org.bouncycastle.asn1.x509.ObjectDigestInfo;
 import org.bouncycastle.jce.PrincipalUtil;
 import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Selector;
 
+import javax.security.auth.x500.X500Principal;
 import java.io.IOException;
 import java.math.BigInteger;
-
+import java.security.MessageDigest;
 import java.security.Principal;
 import java.security.cert.CertSelector;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateParsingException;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.security.auth.x500.X500Principal;
-
 /**
  * The Holder object.
+ * 
  * <pre>
- *  Holder ::= SEQUENCE {
- *        baseCertificateID   [0] IssuerSerial OPTIONAL,
- *                 -- the issuer and serial number of
- *                 -- the holder's Public Key Certificate
- *        entityName          [1] GeneralNames OPTIONAL,
- *                 -- the name of the claimant or role
- *        objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
- *                 -- used to directly authenticate the holder,
- *                 -- for example, an executable
- *  }
+ *          Holder ::= SEQUENCE {
+ *                baseCertificateID   [0] IssuerSerial OPTIONAL,
+ *                         -- the issuer and serial number of
+ *                         -- the holder's Public Key Certificate
+ *                entityName          [1] GeneralNames OPTIONAL,
+ *                         -- the name of the claimant or role
+ *                objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
+ *                         -- used to directly authenticate the holder,
+ *                         -- for example, an executable
+ *          }
  * </pre>
- * This holder currently supports use of the baseCertificateID and the entityName.
+ * 
  */
-public class AttributeCertificateHolder 
-    implements CertSelector
+public class AttributeCertificateHolder
+    implements CertSelector, Selector
 {
-    final Holder   holder;
+    final Holder holder;
 
-    AttributeCertificateHolder(
-        ASN1Sequence seq)
+    AttributeCertificateHolder(ASN1Sequence seq)
     {
         holder = Holder.getInstance(seq);
     }
 
-    public AttributeCertificateHolder(
-        X509Principal issuerName,
-        BigInteger    serialNumber)
+    public AttributeCertificateHolder(X509Principal issuerName,
+        BigInteger serialNumber)
     {
         holder = new org.bouncycastle.asn1.x509.Holder(new IssuerSerial(
-                new GeneralNames(new DERSequence(new GeneralName(issuerName))),
-                new DERInteger(serialNumber)));        
+            new GeneralNames(new DERSequence(new GeneralName(issuerName))),
+            new DERInteger(serialNumber)));
     }
 
-    public AttributeCertificateHolder(
-        X500Principal issuerName,
-        BigInteger    serialNumber)
+    public AttributeCertificateHolder(X500Principal issuerName,
+        BigInteger serialNumber)
     {
         this(X509Util.convertPrincipal(issuerName), serialNumber);
     }
-    
-    public AttributeCertificateHolder(
-        X509Certificate cert) 
+
+    public AttributeCertificateHolder(X509Certificate cert)
         throws CertificateParsingException
-    {        
-        X509Principal   name;
-        
+    {
+        X509Principal name;
+
         try
         {
             name = PrincipalUtil.getIssuerX509Principal(cert);
@@ -82,30 +82,129 @@
         {
             throw new CertificateParsingException(e.getMessage());
         }
-        
-        holder = new Holder(new IssuerSerial(generateGeneralNames(name), new DERInteger(cert.getSerialNumber())));
+
+        holder = new Holder(new IssuerSerial(generateGeneralNames(name),
+            new DERInteger(cert.getSerialNumber())));
     }
-    
-    public AttributeCertificateHolder(
-        X509Principal principal) 
-    {        
+
+    public AttributeCertificateHolder(X509Principal principal)
+    {
         holder = new Holder(generateGeneralNames(principal));
     }
 
-    public AttributeCertificateHolder(
-        X500Principal principal) 
+    public AttributeCertificateHolder(X500Principal principal)
     {
         this(X509Util.convertPrincipal(principal));
     }
-    
+
+    /**
+     * Constructs a holder for v2 attribute certificates with a hash value for
+     * some type of object.
+     * <p>
+     * <code>digestedObjectType</code> can be one of the following:
+     * <ul>
+     * <li>0 - publicKey - A hash of the public key of the holder must be
+     * passed.
+     * <li>1 - publicKeyCert - A hash of the public key certificate of the
+     * holder must be passed.
+     * <li>2 - otherObjectDigest - A hash of some other object type must be
+     * passed. <code>otherObjectTypeID</code> must not be empty.
+     * </ul>
+     * <p>
+     * This cannot be used if a v1 attribute certificate is used.
+     * 
+     * @param digestedObjectType The digest object type.
+     * @param digestAlgorithm The algorithm identifier for the hash.
+     * @param otherObjectTypeID The object type ID if
+     *            <code>digestedObjectType</code> is
+     *            <code>otherObjectDigest</code>.
+     * @param objectDigest The hash value.
+     */
+    public AttributeCertificateHolder(int digestedObjectType,
+        String digestAlgorithm, String otherObjectTypeID, byte[] objectDigest)
+    {
+        holder = new Holder(new ObjectDigestInfo(digestedObjectType,
+            otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays
+                .clone(objectDigest)));
+    }
+
+    /**
+     * Returns the digest object type if an object digest info is used.
+     * <p>
+     * <ul>
+     * <li>0 - publicKey - A hash of the public key of the holder must be
+     * passed.
+     * <li>1 - publicKeyCert - A hash of the public key certificate of the
+     * holder must be passed.
+     * <li>2 - otherObjectDigest - A hash of some other object type must be
+     * passed. <code>otherObjectTypeID</code> must not be empty.
+     * </ul>
+     * 
+     * @return The digest object type or -1 if no object digest info is set.
+     */
+    public int getDigestedObjectType()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            return holder.getObjectDigestInfo().getDigestedObjectType()
+                .getValue().intValue();
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the other object type ID if an object digest info is used.
+     * 
+     * @return The other object type ID or <code>null</code> if no object
+     *         digest info is set.
+     */
+    public String getDigestAlgorithm()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            holder.getObjectDigestInfo().getDigestAlgorithm().getObjectId()
+                .getId();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the hash if an object digest info is used.
+     * 
+     * @return The hash or <code>null</code> if no object digest info is set.
+     */
+    public byte[] getObjectDigest()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            holder.getObjectDigestInfo().getObjectDigest().getBytes();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the digest algorithm ID if an object digest info is used.
+     * 
+     * @return The digest algorithm ID or <code>null</code> if no object
+     *         digest info is set.
+     */
+    public String getOtherObjectTypeID()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            holder.getObjectDigestInfo().getOtherObjectTypeID().getId();
+        }
+        return null;
+    }
+
     private GeneralNames generateGeneralNames(X509Principal principal)
     {
         return new GeneralNames(new DERSequence(new GeneralName(principal)));
     }
-    
+
     private boolean matchesDN(X509Principal subject, GeneralNames targets)
     {
-        GeneralName[]   names = targets.getNames();
+        GeneralName[] names = targets.getNames();
 
         for (int i = 0; i != names.length; i++)
         {
@@ -115,7 +214,8 @@
             {
                 try
                 {
-                    if (new X509Principal(((ASN1Encodable)gn.getName()).getEncoded()).equals(subject))
+                    if (new X509Principal(((ASN1Encodable)gn.getName())
+                        .getEncoded()).equals(subject))
                     {
                         return true;
                     }
@@ -129,18 +229,18 @@
         return false;
     }
 
-    private Object[] getNames(
-        GeneralName[] names)
-    {        
-        List        l = new ArrayList(names.length);
-        
+    private Object[] getNames(GeneralName[] names)
+    {
+        List l = new ArrayList(names.length);
+
         for (int i = 0; i != names.length; i++)
         {
             if (names[i].getTagNo() == GeneralName.directoryName)
             {
                 try
                 {
-                    l.add(new X500Principal(((ASN1Encodable)names[i].getName()).getEncoded()));
+                    l.add(new X500Principal(
+                        ((ASN1Encodable)names[i].getName()).getEncoded()));
                 }
                 catch (IOException e)
                 {
@@ -151,13 +251,12 @@
 
         return l.toArray(new Object[l.size()]);
     }
-    
-    private Principal[] getPrincipals(
-        GeneralNames    names)
+
+    private Principal[] getPrincipals(GeneralNames names)
     {
-        Object[]    p = this.getNames(names.getNames());
-        List        l = new ArrayList();
-        
+        Object[] p = this.getNames(names.getNames());
+        List l = new ArrayList();
+
         for (int i = 0; i != p.length; i++)
         {
             if (p[i] instanceof Principal)
@@ -165,14 +264,16 @@
                 l.add(p[i]);
             }
         }
-        
+
         return (Principal[])l.toArray(new Principal[l.size()]);
     }
-    
+
     /**
-     * Return any principal objects inside the attribute certificate holder entity names field.
+     * Return any principal objects inside the attribute certificate holder
+     * entity names field.
      * 
-     * @return an array of Principal objects (usually X500Principal), null if no entity names field is set.
+     * @return an array of Principal objects (usually X500Principal), null if no
+     *         entity names field is set.
      */
     public Principal[] getEntityNames()
     {
@@ -180,10 +281,10 @@
         {
             return getPrincipals(holder.getEntityName());
         }
-        
+
         return null;
     }
-    
+
     /**
      * Return the principals associated with the issuer attached to this holder
      * 
@@ -195,14 +296,16 @@
         {
             return getPrincipals(holder.getBaseCertificateID().getIssuer());
         }
-        
+
         return null;
     }
-    
+
     /**
-     * Return the serial number associated with the issuer attached to this holder.
+     * Return the serial number associated with the issuer attached to this
+     * holder.
      * 
-     * @return the certificate serial number, null if no BaseCertificateID is set.
+     * @return the certificate serial number, null if no BaseCertificateID is
+     *         set.
      */
     public BigInteger getSerialNumber()
     {
@@ -210,30 +313,25 @@
         {
             return holder.getBaseCertificateID().getSerial().getValue();
         }
-        
+
         return null;
     }
-    
-    /* (non-Javadoc)
-     * @see java.security.cert.CertSelector#clone()
-     */
+
     public Object clone()
     {
-        return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Object());
+        return new AttributeCertificateHolder((ASN1Sequence)holder
+            .toASN1Object());
     }
 
-    /* (non-Javadoc)
-     * @see java.security.cert.CertSelector#match(java.security.cert.Certificate)
-     */
     public boolean match(Certificate cert)
     {
         if (!(cert instanceof X509Certificate))
         {
             return false;
         }
-        
+
         X509Certificate x509Cert = (X509Certificate)cert;
-        
+
         try
         {
             if (holder.getBaseCertificateID() != null)
@@ -241,23 +339,80 @@
                 return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
                     && matchesDN(PrincipalUtil.getIssuerX509Principal(x509Cert), holder.getBaseCertificateID().getIssuer());
             }
-    
+
             if (holder.getEntityName() != null)
             {
-                if (matchesDN(PrincipalUtil.getSubjectX509Principal(x509Cert), holder.getEntityName()))
+                if (matchesDN(PrincipalUtil.getSubjectX509Principal(x509Cert),
+                    holder.getEntityName()))
                 {
                     return true;
                 }
             }
+            if (holder.getObjectDigestInfo() != null)
+            {
+                MessageDigest md = null;
+                try
+                {
+                    md = MessageDigest.getInstance(getDigestAlgorithm(), "BC");
+
+                }
+                catch (Exception e)
+                {
+                    return false;
+                }
+                switch (getDigestedObjectType())
+                {
+                case ObjectDigestInfo.publicKey:
+                    // TODO: DSA Dss-parms
+                    md.update(cert.getPublicKey().getEncoded());
+                    break;
+                case ObjectDigestInfo.publicKeyCert:
+                    md.update(cert.getEncoded());
+                    break;
+                }
+                if (!Arrays.areEqual(md.digest(), getObjectDigest()))
+                {
+                    return false;
+                }
+            }
         }
         catch (CertificateEncodingException e)
         {
             return false;
         }
-        
-        /**
-         * objectDigestInfo not supported
-         */
+
         return false;
     }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (!(obj instanceof AttributeCertificateHolder))
+        {
+            return false;
+        }
+
+        AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
+
+        return this.holder.equals(other.holder);
+    }
+
+    public int hashCode()
+    {
+        return this.holder.hashCode();
+    }
+
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509Certificate))
+        {
+            return false;
+        }
+
+        return match((Certificate)obj);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/x509/AttributeCertificateIssuer.java b/src/main/java/org/bouncycastle/x509/AttributeCertificateIssuer.java
index 0679a5c..9960c74 100644
--- a/src/main/java/org/bouncycastle/x509/AttributeCertificateIssuer.java
+++ b/src/main/java/org/bouncycastle/x509/AttributeCertificateIssuer.java
@@ -1,5 +1,15 @@
 package org.bouncycastle.x509;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AttCertIssuer;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.V2Form;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.util.Selector;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.IOException;
 import java.security.Principal;
 import java.security.cert.CertSelector;
@@ -8,51 +18,39 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AttCertIssuer;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.V2Form;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.X509Principal;
-
 /**
  * Carrying class for an attribute certificate issuer.
  */
 public class AttributeCertificateIssuer
-    implements CertSelector
+    implements CertSelector, Selector
 {
-    final ASN1Encodable  form;
-    
+    final ASN1Encodable form;
+
     /**
-     * @param issuer
+     * Set the issuer directly with the ASN.1 structure.
+     * 
+     * @param issuer The issuer
      */
-    AttributeCertificateIssuer(
-        AttCertIssuer issuer)
+    public AttributeCertificateIssuer(AttCertIssuer issuer)
     {
         form = issuer.getIssuer();
     }
 
-    public AttributeCertificateIssuer(
-        X500Principal principal) 
-        throws IOException 
-    {        
+    public AttributeCertificateIssuer(X500Principal principal)
+        throws IOException
+    {
         this(new X509Principal(principal.getEncoded()));
     }
-    
-    public AttributeCertificateIssuer(
-        X509Principal principal) 
-    {        
+
+    public AttributeCertificateIssuer(X509Principal principal)
+    {
         form = new V2Form(new GeneralNames(new DERSequence(new GeneralName(principal))));
     }
-    
+
     private Object[] getNames()
     {
-        GeneralNames    name;
-        
+        GeneralNames name;
+
         if (form instanceof V2Form)
         {
             name = ((V2Form)form).getIssuerName();
@@ -61,18 +59,19 @@
         {
             name = (GeneralNames)form;
         }
-        
-        GeneralName[]   names = name.getNames();
-        
-        List        l = new ArrayList(names.length);
-        
+
+        GeneralName[] names = name.getNames();
+
+        List l = new ArrayList(names.length);
+
         for (int i = 0; i != names.length; i++)
         {
             if (names[i].getTagNo() == GeneralName.directoryName)
             {
                 try
                 {
-                    l.add(new X500Principal(((ASN1Encodable)names[i].getName()).getEncoded()));
+                    l.add(new X500Principal(
+                        ((ASN1Encodable)names[i].getName()).getEncoded()));
                 }
                 catch (IOException e)
                 {
@@ -80,20 +79,21 @@
                 }
             }
         }
-        
+
         return l.toArray(new Object[l.size()]);
     }
-    
+
     /**
-     * Return any principal objects inside the attribute certificate issuer object.
+     * Return any principal objects inside the attribute certificate issuer
+     * object.
      * 
      * @return an array of Principal objects (usually X500Principal)
      */
     public Principal[] getPrincipals()
     {
-        Object[]    p = this.getNames();
-        List        l = new ArrayList();
-        
+        Object[] p = this.getNames();
+        List l = new ArrayList();
+
         for (int i = 0; i != p.length; i++)
         {
             if (p[i] instanceof Principal)
@@ -104,10 +104,10 @@
 
         return (Principal[])l.toArray(new Principal[l.size()]);
     }
-    
+
     private boolean matchesDN(X500Principal subject, GeneralNames targets)
     {
-        GeneralName[]   names = targets.getNames();
+        GeneralName[] names = targets.getNames();
 
         for (int i = 0; i != names.length; i++)
         {
@@ -130,27 +130,21 @@
 
         return false;
     }
-    
-    /* (non-Javadoc)
-     * @see java.security.cert.CertSelector#clone()
-     */
+
     public Object clone()
     {
         return new AttributeCertificateIssuer(AttCertIssuer.getInstance(form));
     }
-    
-    /* (non-Javadoc)
-     * @see java.security.cert.CertSelector#match(java.security.cert.Certificate)
-     */
+
     public boolean match(Certificate cert)
     {
         if (!(cert instanceof X509Certificate))
         {
             return false;
         }
-        
+
         X509Certificate x509Cert = (X509Certificate)cert;
-        
+
         if (form instanceof V2Form)
         {
             V2Form issuer = (V2Form)form;
@@ -159,7 +153,7 @@
                 return issuer.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
                     && matchesDN(x509Cert.getIssuerX500Principal(), issuer.getBaseCertificateID().getIssuer());
             }
-            
+
             GeneralNames name = issuer.getIssuerName();
             if (matchesDN(x509Cert.getSubjectX500Principal(), name))
             {
@@ -177,4 +171,36 @@
 
         return false;
     }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (!(obj instanceof AttributeCertificateIssuer))
+        {
+            return false;
+        }
+
+        AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj;
+
+        return this.form.equals(other.form);
+    }
+
+    public int hashCode()
+    {
+        return this.form.hashCode();
+    }
+
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509Certificate))
+        {
+            return false;
+        }
+
+        return match((Certificate)obj);
+    }
 }
diff --git a/src/main/java/org/bouncycastle/x509/CertPathReviewerException.java b/src/main/java/org/bouncycastle/x509/CertPathReviewerException.java
deleted file mode 100644
index 173d478..0000000
--- a/src/main/java/org/bouncycastle/x509/CertPathReviewerException.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.bouncycastle.x509;
-
-import java.security.cert.CertPath;
-
-import org.bouncycastle.i18n.ErrorBundle;
-import org.bouncycastle.i18n.LocalizedException;
-
-public class CertPathReviewerException extends LocalizedException
-{
-
-    private int index = -1;
-    
-    private CertPath certPath = null;
-    
-    public CertPathReviewerException(ErrorBundle errorMessage, Throwable throwable)
-    {
-        super(errorMessage, throwable);
-    }
-
-    public CertPathReviewerException(ErrorBundle errorMessage)
-    {
-        super(errorMessage);
-    }
-
-    public CertPathReviewerException(
-            ErrorBundle errorMessage, 
-            Throwable throwable,
-            CertPath certPath,
-            int index)
-    {
-        super(errorMessage, throwable);
-        if (certPath == null || index == -1)
-        {
-            throw new IllegalArgumentException();
-        }
-        if (index < -1 || (certPath != null && index >= certPath.getCertificates().size()))
-        {
-            throw new IndexOutOfBoundsException();
-        }
-        this.certPath = certPath;
-        this.index = index;
-    }
-    
-    public CertPathReviewerException(
-            ErrorBundle errorMessage, 
-            CertPath certPath,
-            int index)
-    {
-        super(errorMessage);
-        if (certPath == null || index == -1)
-        {
-            throw new IllegalArgumentException();
-        }
-        if (index < -1 || (certPath != null && index >= certPath.getCertificates().size()))
-        {
-            throw new IndexOutOfBoundsException();
-        }
-        this.certPath = certPath;
-        this.index = index;
-    }
-    
-    public CertPath getCertPath()
-    {
-        return certPath;
-    }
-    
-    public int getIndex()
-    {
-        return index;
-    }
-
-}
diff --git a/src/main/java/org/bouncycastle/x509/CertPathReviewerMessages.properties b/src/main/java/org/bouncycastle/x509/CertPathReviewerMessages.properties
index 9df5e89..6843d2c 100644
--- a/src/main/java/org/bouncycastle/x509/CertPathReviewerMessages.properties
+++ b/src/main/java/org/bouncycastle/x509/CertPathReviewerMessages.properties
@@ -59,7 +59,7 @@
 
 # error processing the subject alternative name extension
 CertPathReviewer.subjAltNameExtError.title = Name constraint checking failed
-CertPathReviewer.subjAltNameExtError.text = Name constraint checking failed: there was an error processing the subject alernative name extension of the certificate.
+CertPathReviewer.subjAltNameExtError.text = Name constraint checking failed: there was an error processing the subject alternative name extension of the certificate.
 CertPathReviewer.subjAltNameExtError.summary = Error processing the subject alternative name extension.
 CertPathReviewer.subjAltNameExtError.details = Name constraint checking failed: there was an error processing the subject alternative name extension of the certificate.
 
@@ -115,33 +115,40 @@
 # error processing critical extension
 # {0} the message of the underlying exception
 # {1} the underlying exception
+# {2} the name of the exception
 CertPathReviewer.criticalExtensionError.title = Error processing a critical extension
-CertPathReviewer.criticalExtensionError.text = Error processing a critical extension. Cause: {0}.
-CertPathReviewer.criticalExtensionError.summary = Error processing a critical extension. Cause: {0}.
-CertPathReviewer.criticalExtensionError.details = Error processing a critical extension. Cause: {0}.
+CertPathReviewer.criticalExtensionError.text = Error processing a critical extension. A {0} occurred.
+CertPathReviewer.criticalExtensionError.summary = Error processing a critical extension. A {0} occurred.
+CertPathReviewer.criticalExtensionError.details = Error processing a critical extension. A {0} occurred. Cause: {0}.
 
 # error initializing the certpath checkers
 # {0} the message of the underlying exception
 # {1} the underlying exception
+# {2} the name of the exception
 CertPathReviewer.certPathCheckerError.title = Checking critical extensions failed
-CertPathReviewer.certPathCheckerError.text = Checking critical extensions failed: there was an error initializing a CertPathChecker.
-CertPathReviewer.certPathCheckerError.summary = Checking critical extensions failed: error initializing a CertPathChecker
-CertPathReviewer.certPathCheckerError.details = Checking critical extensions failed: there was an error initializing a CertPathChecker. Cause: {0}
+CertPathReviewer.certPathCheckerError.text = Checking critical extensions failed: there was a {2} initializing a CertPathChecker.
+CertPathReviewer.certPathCheckerError.summary = Checking critical extensions failed: {2} initializing a CertPathChecker
+CertPathReviewer.certPathCheckerError.details = Checking critical extensions failed: there was an {2} initializing a CertPathChecker. Cause: {0}
 
 
 ## check signature errors
 
+CertPathReviewer.rootKeyIsValidButNotATrustAnchor.title = Root key with valid signature but no trust anchor
+CertPathReviewer.rootKeyIsValidButNotATrustAnchor.text = The certificate has a valid signature, but is no trust anchor
+CertPathReviewer.rootKeyIsValidButNotATrustAnchor.summary = The certificate has a valid signature, but is no trust anchor
+CertPathReviewer.rootKeyIsValidButNotATrustAnchor.details = The certificate has a valid signature, but is no trust anchor
+
 # trustanchor found, but certificate validation failed
-CertPathReviewer.trustButInvalidCert.title = TrustAnchor found, but certificate invalid
-CertPathReviewer.trustButInvalidCert.text = A TrustAnchor was found but the certificate validation failed.
-CertPathReviewer.trustButInvalidCert.summary = TrustAnchor found but certificate validation failed.
-CertPathReviewer.trustButInvalidCert.details = A TrustAnchor was found but the certificate validation failed.
+CertPathReviewer.trustButInvalidCert.title = Trust anchor found, but different public key
+CertPathReviewer.trustButInvalidCert.text = A trust anchor was found. But it has a different public key, than was used to issue the first certificate of the cert path.
+CertPathReviewer.trustButInvalidCert.summary = A trust anchor was found. But it has a different public key, than was used to issue the first certificate of the cert path.
+CertPathReviewer.trustButInvalidCert.details = A trust anchor was found. But it has a different public key, than was used to issue the first certificate of the cert path.
 
 # trustanchor - cannot extract issuer
-CertPathReviewer.trustAnchorIssuerError.title = Finding TrustAnchor failed 
-CertPathReviewer.trustAnchorIssuerError.text = Finding TrustAnchor failed: cannot extract issuer from certificate.
-CertPathReviewer.trustAnchorIssuerError.summary = Finding TrustAnchor failed: cannot extract issuer from certificate.
-CertPathReviewer.trustAnchorIssuerError.details = Finding TrustAnchor failed: cannot extract issuer from certificate.
+CertPathReviewer.trustAnchorIssuerError.title = Finding trust anchor failed 
+CertPathReviewer.trustAnchorIssuerError.text = Finding trust anchor failed: cannot extract issuer from certificate.
+CertPathReviewer.trustAnchorIssuerError.summary = Finding trust anchor failed: cannot extract issuer from certificate.
+CertPathReviewer.trustAnchorIssuerError.details = Finding trust anchor failed: cannot extract issuer from certificate.
 
 # no trustanchor was found for the certificate path
 # {0} issuer of the root certificate of the path
@@ -181,10 +188,11 @@
 # signature can not be verified
 # {0} message of the underlying exception (english)
 # {1} the underlying exception
+# {2} the name of the exception
 CertPathReviewer.signatureNotVerified.title = Certificate signature invalid
-CertPathReviewer.signatureNotVerified.text = The certificate signature is invalid.
+CertPathReviewer.signatureNotVerified.text = The certificate signature is invalid. A {2} occurred.
 CertPathReviewer.signatureNotVerified.summary = The certificate signature is invalid.
-CertPathReviewer.signatureNotVerified.details = The certificate signature is invalid. Cause: {0}
+CertPathReviewer.signatureNotVerified.details = The certificate signature is invalid. A {2} occurred. Cause: {0}
 
 # certificate expired
 # {0} the date the certificate expired 
@@ -196,9 +204,9 @@
 # certificate not yet valid
 # {0} the date from which on the certificate is valid
 CertPathReviewer.certificateNotYetValid.title = Certificate is not yet valid
-CertPathReviewer.certificateNotYetValid.text = Could not validate the certificate. Certificate is not valid untill {0,date} {0,time,full}.
-CertPathReviewer.certificateNotYetValid.summary = Certificate is not valid untill {0,date} {0,time,full}.
-CertPathReviewer.certificateNotYetValid.details = Could not validate the certificate. Certificate is not valid untill {0,date} {0,time,full}. 
+CertPathReviewer.certificateNotYetValid.text = Could not validate the certificate. Certificate is not valid until {0,date} {0,time,full}.
+CertPathReviewer.certificateNotYetValid.summary = Certificate is not valid until {0,date} {0,time,full}.
+CertPathReviewer.certificateNotYetValid.details = Could not validate the certificate. Certificate is not valid until {0,date} {0,time,full}. 
 
 # certificate invalid issuer DN
 # {0} expected issuer DN as String
@@ -241,13 +249,20 @@
 
 ## check signatures notifications
 
+#
+# trust anchor has no keyusage certSign
+CertPathReviewer.trustKeyUsage.title = Trust anchor key usage
+CertPathReviewer.trustKeyUsage.text = The trust anchor is not alloed to sign certificates. 
+CertPathReviewer.trustKeyUsage.summary = The trust anchor is not alloed to sign certificates.
+CertPathReviewer.trustKeyUsage.details = The trust anchor is not alloed to sign certificates.
+
 # certificate path validation date
 # {0} date for which the cert path is validated
 # {1} current date
 CertPathReviewer.certPathValidDate.title = Certificate path validation date
-CertPathReviewer.certPathValidDate.text = The certificate path was applied on {0,date} {0,time,full}. It was validated at {1,date} {1,time,full}.
-CertPathReviewer.certPathValidDate.summary = The certificate path was validated for {0,date} {0,time,full}. It was validated at {1,date} {1,time,full}.
-CertPathReviewer.certPathValidDate.details = The certificate path was validated for {0,date} {0,time,full}. It was validated at {1,date} {1,time,full}.
+CertPathReviewer.certPathValidDate.text = The certificate path was applied on {0,date} {0,time,full}. It was checked at {1,date} {1,time,full}.
+CertPathReviewer.certPathValidDate.summary = The certificate path was validated for {0,date} {0,time,full}. It was checked at {1,date} {1,time,full}.
+CertPathReviewer.certPathValidDate.details = The certificate path was validated for {0,date} {0,time,full}. It was checked at {1,date} {1,time,full}.
 
 
 ## check policy errors
@@ -272,9 +287,9 @@
 
 # error processing inhibit any policy extension
 CertPathReviewer.policyInhibitExtError.title = Policy checking failed
-CertPathReviewer.policyInhibitExtError.text = Policy checking failed: there was an error processing the policy mapping extension.
+CertPathReviewer.policyInhibitExtError.text = Policy checking failed: there was an error processing the inhibit any policy extension.
 CertPathReviewer.policyInhibitExtError.summary = Error processing the inhibit any policy extension.
-CertPathReviewer.policyInhibitExtError.details = Policy checking failed: there was an error processing the policy mapping extension.
+CertPathReviewer.policyInhibitExtError.details = Policy checking failed: there was an error processing the inhibit any policy extension.
 
 # error building qualifier set
 CertPathReviewer.policyQualifierError.title = Policy checking failed
@@ -343,6 +358,15 @@
 CertPathReviewer.onlineInvalidCRL.summary = The CRL loaded from {2} was outdated. Issued on {0,date}, next update on {1,date}.
 CertPathReviewer.onlineInvalidCRL.details = The CRL loaded from {2} was outdated. Issued on {0,date}, next update on {1,date}.
 
+#found a CRL at a crl distribution point, but issued by another CA
+# {0} issuer of the CRL
+# {1} expected issuer
+# {2} the url of the distribution point
+CertPathReviewer.onlineCRLWrongCA.title = CRL from wrong issuer at CRL distribution point
+CertPathReviewer.onlineCRLWrongCA.text = The CRL loaded from {2} has was issued by {0}, excpected {1}.
+CertPathReviewer.onlineCRLWrongCA.summary = The CRL loaded from {2} has a wrong issuer.
+CertPathReviewer.onlineCRLWrongCA.details = The CRL loaded from {2} has was issued by {0}, excpected {1}.
+
 # Certificate not revoked
 CertPathReviewer.notRevoked.title = Certificate not revoked
 CertPathReviewer.notRevoked.text = The certificate was not revoked.
@@ -380,21 +404,22 @@
 
 # unable to get crl from crl distribution point
 # {0} the url of the distribution point
-# {1} the message of the occured exception
-# {2} the occured exception
+# {1} the message of the occurred exception
+# {2} the occurred exception
+# {3} the name of the exception
 CertPathReviewer.loadCrlDistPointError.title = Cannot load CRL from CRL distribution point
-CertPathReviewer.loadCrlDistPointError.text = Unable to load a CRL from: {0}. An Exception occured.
-CertPathReviewer.loadCrlDistPointError.summary = Unable to load a CRL from: {0}. An Exception occured.
-CertPathReviewer.loadCrlDistPointError.details = Unable to load a CRL from: {0}. An Exception occured: Cause: {1}.
+CertPathReviewer.loadCrlDistPointError.text = Unable to load a CRL from: {0}. A {3} occurred.
+CertPathReviewer.loadCrlDistPointError.summary = Unable to load a CRL from: {0}. A {3} occurred.
+CertPathReviewer.loadCrlDistPointError.details = Unable to load a CRL from: {0}. A {3} occurred. Cause: {1}.
 
 # no crl found in certstores
 # {0} the issuers which we searched for
 # {1} list of crl issuer names that are found in the certstores
 # {2} number of crls in the certstores
-CertPathReviewer.noCrlInCertstore.title = No matching CRL found in local certstores
-CertPathReviewer.noCrlInCertstore.text = No matching CRL was found in the provided local certstore.
-CertPathReviewer.noCrlInCertstore.summary = No matching CRL was found in the provided local certstore.
-CertPathReviewer.noCrlInCertstore.details = No matching CRL was found in the provided local certstore. \
+CertPathReviewer.noCrlInCertstore.title = No matching CRL found in local CRL store
+CertPathReviewer.noCrlInCertstore.text = No matching CRL was found in the provided local CRL store.
+CertPathReviewer.noCrlInCertstore.summary = No matching CRL was found in the provided local CRL store.
+CertPathReviewer.noCrlInCertstore.details = No matching CRL was found in the provided local CRL store. \
 No CRL was found for the selector "{0}". The {2} CRL(s) in the certstores are from "{1}".
 
 
@@ -409,10 +434,11 @@
 # cannot extract crls
 # {0} message from the underlying exception
 # {1} the underlying exception
+# {2} the name of the exception
 CertPathReviewer.crlExtractionError.title = CRL checking failed
-CertPathReviewer.crlExtractionError.text = CRL checking failed: Cannot extract CRL from CertStore.
-CertPathReviewer.crlExtractionError.summary = CRL checking failed: Cannot extract CRL from CertStore.
-CertPathReviewer.crlExtractionError.details = CRL checking failed: Cannot extract CRL from CertStore. Cause: {0}.
+CertPathReviewer.crlExtractionError.text = CRL checking failed: Cannot extract CRL from CertStore. There was a {2}.
+CertPathReviewer.crlExtractionError.summary = CRL checking failed: Cannot extract CRL from CertStore. There was a {2}.
+CertPathReviewer.crlExtractionError.details = CRL checking failed: Cannot extract CRL from CertStore. There was a {2}. Cause: {0}.
 
 # Issuer certificate key usage extension does not permit crl signing
 CertPathReviewer.noCrlSigningPermited.title = CRL checking failed
@@ -448,9 +474,9 @@
 # {0} the date the certificate was revoked
 # {1} the reason for revoking the certificate
 CertPathReviewer.certRevoked.title = Certificate was revoked
-CertPathReviewer.certRevoked.text = The certificate is invalid, because it was revoked at {0,date} {0,time,full}. Reason: {1}.
-CertPathReviewer.certRevoked.summary = The certificate is invalid, because it was revoked at {0,date} {0,time,full}.
-CertPathReviewer.certRevoked.details = The certificate is invalid, because it was revoked at {0,date} {0,time,full}. Reason: {1}.
+CertPathReviewer.certRevoked.text = The certificate was revoked at {0,date} {0,time,full}. Reason: {1}.
+CertPathReviewer.certRevoked.summary = The certificate was revoked at {0,date} {0,time,full}.
+CertPathReviewer.certRevoked.details = The certificate was revoked at {0,date} {0,time,full}. Reason: {1}.
 
 # error processing issuing distribution point extension
 CertPathReviewer.distrPtExtError.title = CRL checking failed
@@ -528,24 +554,24 @@
 # {1} limit value
 # {2} monetary value as MonetaryValue
 CertPathReviewer.QcLimitValueAlpha.title = Transaction Value Limit
-CertPathReviewer.QcLimitValueAlpha.text = This certificate has a limit for the transaction value: {1,number,currency} {0}.
-CertPathReviewer.QcLimitValueAlpha.summary = Transaction value limit: {1,number,currency} {0}.
-CertPathReviewer.QcLimitValueAlpha.details = This certificate has a limitation on the value of transaction for which this certificate can be used to the specified amount, according to the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures, as implemented in the law of the country specified in the issuer field of this certificate. The limit for this certificate is {1,number,currency} {0}.
+CertPathReviewer.QcLimitValueAlpha.text = This certificate has a limit for the transaction value: {1,number, ###,###,###,##0.00#} {0}.
+CertPathReviewer.QcLimitValueAlpha.summary = Transaction value limit: {1,number, ###,###,###,##0.00#} {0}.
+CertPathReviewer.QcLimitValueAlpha.details = This certificate has a limitation on the value of transaction for which this certificate can be used to the specified amount, according to the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures, as implemented in the law of the country specified in the issuer field of this certificate. The limit for this certificate is {1,number, ###,###,###,##0.00#} {0}.
 
 # QcLimitValue Numeric currency code
 # {0} currency code
 # {1} limit value
 # {2} monetary value as MonetaryValue
 CertPathReviewer.QcLimitValueNum.title = Transaction Value Limit
-CertPathReviewer.QcLimitValueNum.text = This certificate has a limit for the transaction value: {1,number,currency} of currency {0} (See RFC 4217 for currency codes).
-CertPathReviewer.QcLimitValueNum.summary = Transaction value limit: {1,number,currency} of currency {0} (See RFC 4217 for currency codes).
-CertPathReviewer.QcLimitValueNum.details = This certificate has a limitation on the value of transaction for which this certificate can be used to the specified amount, according to the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures, as implemented in the law of the country specified in the issuer field of this certificate. The limit for this certificate is {1,number,currency} of currency {0} (See RFC 4217 for currency codes).
+CertPathReviewer.QcLimitValueNum.text = This certificate has a limit for the transaction value: {1,number, ###,###,###,##0.00#} of currency {0} (See RFC 4217 for currency codes).
+CertPathReviewer.QcLimitValueNum.summary = Transaction value limit: {1,number, ###,###,###,##0.00#} of currency {0} (See RFC 4217 for currency codes).
+CertPathReviewer.QcLimitValueNum.details = This certificate has a limitation on the value of transaction for which this certificate can be used to the specified amount, according to the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures, as implemented in the law of the country specified in the issuer field of this certificate. The limit for this certificate is {1,number, ###,###,###,##0.00#} of currency {0} (See RFC 4217 for currency codes).
 
 # QcSSCD
 CertPathReviewer.QcSSCD.title = QcSSCD Statement
-CertPathReviewer.QcSSCD.text = The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
-CertPathReviewer.QcSSCD.summary = The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
-CertPathReviewer.QcSSCD.details = The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
+CertPathReviewer.QcSSCD.text = (SSCD) The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
+CertPathReviewer.QcSSCD.summary = (SSCD) The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
+CertPathReviewer.QcSSCD.details = (SSCD) The issuer claims that for the certificate where this statement appears that the private key associated with the public key in the certificate is protected according to Annex III of the Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures.
 
 # QcEuCompliance
 CertPathReviewer.QcEuCompliance.title = Qualified Certificate
@@ -561,3 +587,30 @@
 CertPathReviewer.QcStatementExtError.summary = Error processing the qc statements extension.
 CertPathReviewer.QcStatementExtError.details = Error processing the qc statements extension.
 
+## unknown/generic errors
+CertPathReviewer.unknown.title = Unexpected Error 
+CertPathReviewer.unknown.text = Unexpected Error {0}
+CertPathReviewer.unknown.summary = Unexpected Error 
+CertPathReviewer.unknown.details = Unexpected Error {0}
+
+#
+# crl reasons
+#
+unspecified = Unspecified
+keyCompromise = Key Compromise
+cACompromise = CA Compromise
+affiliationChanged = Affiliation Changed
+superseded = Superseded
+cessationOfOperation = Cessation of Operation
+certificateHold = Certificate Hold
+unknown = Unknown
+removeFromCRL = Remove from CRL
+privilegeWithdrawn = Privilege Withdrawn
+aACompromise = AA Compromise
+
+#
+#
+#
+missingIssuer = The missing certificate was issued by
+missingSerial = with the serial number
+ 
\ No newline at end of file
diff --git a/src/main/java/org/bouncycastle/x509/ExtCertificateEncodingException.java b/src/main/java/org/bouncycastle/x509/ExtCertificateEncodingException.java
new file mode 100644
index 0000000..a26c310
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/ExtCertificateEncodingException.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.x509;
+
+import java.security.cert.CertificateEncodingException;
+
+class ExtCertificateEncodingException
+    extends CertificateEncodingException
+{
+    Throwable cause;
+
+    ExtCertificateEncodingException(String message, Throwable cause)
+    {
+        super(message);
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/ExtendedPKIXBuilderParameters.java b/src/main/java/org/bouncycastle/x509/ExtendedPKIXBuilderParameters.java
new file mode 100644
index 0000000..51831d0
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/ExtendedPKIXBuilderParameters.java
@@ -0,0 +1,210 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.util.Selector;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class contains extended parameters for PKIX certification path builders.
+ * 
+ * @see java.security.cert.PKIXBuilderParameters
+ * @see org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi
+ */
+public class ExtendedPKIXBuilderParameters extends ExtendedPKIXParameters
+{
+
+    private int maxPathLength = 5;
+
+    private Set excludedCerts = Collections.EMPTY_SET;
+
+    /**
+     * Excluded certificates are not used for building a certification path.
+     * <p>
+     * The returned set is immutable.
+     * 
+     * @return Returns the excluded certificates.
+     */
+    public Set getExcludedCerts()
+    {
+        return Collections.unmodifiableSet(excludedCerts);
+    }
+
+    /**
+     * Sets the excluded certificates which are not used for building a
+     * certification path. If the <code>Set</code> is <code>null</code> an
+     * empty set is assumed.
+     * <p>
+     * The given set is cloned to protect it against subsequent modifications.
+     * 
+     * @param excludedCerts The excluded certificates to set.
+     */
+    public void setExcludedCerts(Set excludedCerts)
+    {
+        if (excludedCerts == null)
+        {
+            excludedCerts = Collections.EMPTY_SET;
+        }
+        else
+        {
+            this.excludedCerts = new HashSet(excludedCerts);
+        }
+    }
+
+    /**
+     * Creates an instance of <code>PKIXBuilderParameters</code> with the
+     * specified <code>Set</code> of most-trusted CAs. Each element of the set
+     * is a {@link TrustAnchor TrustAnchor}.
+     * 
+     * <p>
+     * Note that the <code>Set</code> is copied to protect against subsequent
+     * modifications.
+     * 
+     * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s
+     * @param targetConstraints a <code>Selector</code> specifying the
+     *            constraints on the target certificate or attribute
+     *            certificate.
+     * @throws InvalidAlgorithmParameterException if <code>trustAnchors</code>
+     *             is empty.
+     * @throws NullPointerException if <code>trustAnchors</code> is
+     *             <code>null</code>
+     * @throws ClassCastException if any of the elements of
+     *             <code>trustAnchors</code> is not of type
+     *             <code>java.security.cert.TrustAnchor</code>
+     */
+    public ExtendedPKIXBuilderParameters(Set trustAnchors,
+            Selector targetConstraints)
+            throws InvalidAlgorithmParameterException
+    {
+        super(trustAnchors);
+        setTargetConstraints(targetConstraints);
+    }
+
+    /**
+     * Sets the maximum number of intermediate non-self-issued certificates in a
+     * certification path. The PKIX <code>CertPathBuilder</code> must not
+     * build paths longer then this length.
+     * <p>
+     * A value of 0 implies that the path can only contain a single certificate.
+     * A value of -1 does not limit the length. The default length is 5.
+     * 
+     * <p>
+     * 
+     * The basic constraints extension of a CA certificate overrides this value
+     * if smaller.
+     * 
+     * @param maxPathLength the maximum number of non-self-issued intermediate
+     *            certificates in the certification path
+     * @throws InvalidParameterException if <code>maxPathLength</code> is set
+     *             to a value less than -1
+     * 
+     * @see org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi
+     * @see #getMaxPathLength
+     */
+    public void setMaxPathLength(int maxPathLength)
+    {
+        if (maxPathLength < -1)
+        {
+            throw new InvalidParameterException("The maximum path "
+                    + "length parameter can not be less than -1.");
+        }
+        this.maxPathLength = maxPathLength;
+    }
+
+    /**
+     * Returns the value of the maximum number of intermediate non-self-issued
+     * certificates in the certification path.
+     * 
+     * @return the maximum number of non-self-issued intermediate certificates
+     *         in the certification path, or -1 if no limit exists.
+     * 
+     * @see #setMaxPathLength(int)
+     */
+    public int getMaxPathLength()
+    {
+        return maxPathLength;
+    }
+
+    /**
+     * Can alse handle <code>ExtendedPKIXBuilderParameters</code> and
+     * <code>PKIXBuilderParameters</code>.
+     * 
+     * @param params Parameters to set.
+     * @see org.bouncycastle.x509.ExtendedPKIXParameters#setParams(java.security.cert.PKIXParameters)
+     */
+    protected void setParams(PKIXParameters params)
+    {
+        super.setParams(params);
+        if (params instanceof ExtendedPKIXBuilderParameters)
+        {
+            ExtendedPKIXBuilderParameters _params = (ExtendedPKIXBuilderParameters) params;
+            maxPathLength = _params.maxPathLength;
+            excludedCerts = new HashSet(_params.excludedCerts);
+        }
+        if (params instanceof PKIXBuilderParameters)
+        {
+            PKIXBuilderParameters _params = (PKIXBuilderParameters) params;
+            maxPathLength = _params.getMaxPathLength();
+        }
+    }
+
+    /**
+     * Makes a copy of this <code>PKIXParameters</code> object. Changes to the
+     * copy will not affect the original and vice versa.
+     * 
+     * @return a copy of this <code>PKIXParameters</code> object
+     */
+    public Object clone()
+    {
+        ExtendedPKIXBuilderParameters params = null;
+        try
+        {
+            params = new ExtendedPKIXBuilderParameters(getTrustAnchors(),
+                    getTargetConstraints());
+        }
+        catch (Exception e)
+        {
+            // cannot happen
+            throw new RuntimeException(e.getMessage());
+        }
+        params.setParams(this);
+        return params;
+    }
+
+    /**
+     * Returns an instance of <code>ExtendedPKIXParameters</code> which can be
+     * safely casted to <code>ExtendedPKIXBuilderParameters</code>.
+     * <p>
+     * This method can be used to get a copy from other
+     * <code>PKIXBuilderParameters</code>, <code>PKIXParameters</code>,
+     * and <code>ExtendedPKIXParameters</code> instances.
+     * 
+     * @param pkixParams The PKIX parameters to create a copy of.
+     * @return An <code>ExtendedPKIXBuilderParameters</code> instance.
+     */
+    public static ExtendedPKIXParameters getInstance(PKIXParameters pkixParams)
+    {
+        ExtendedPKIXBuilderParameters params;
+        try
+        {
+            params = new ExtendedPKIXBuilderParameters(pkixParams
+                    .getTrustAnchors(), X509CertStoreSelector
+                    .getInstance((X509CertSelector) pkixParams
+                            .getTargetCertConstraints()));
+        }
+        catch (Exception e)
+        {
+            // cannot happen
+            throw new RuntimeException(e.getMessage());
+        }
+        params.setParams(pkixParams);
+        return params;
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java b/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java
new file mode 100644
index 0000000..6386618
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/ExtendedPKIXParameters.java
@@ -0,0 +1,651 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.CertSelector;
+import java.security.cert.CertStore;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class extends the PKIXParameters with a validity model parameter.
+ */
+public class ExtendedPKIXParameters
+    extends PKIXParameters
+{
+
+    private List stores;
+
+    private Selector selector;
+
+    private boolean additionalLocationsEnabled;
+
+    private List additionalStores;
+
+    private Set trustedACIssuers;
+
+    private Set necessaryACAttributes;
+
+    private Set prohibitedACAttributes;
+
+    private Set attrCertCheckers;
+
+    /**
+     * Creates an instance of <code>PKIXParameters</code> with the specified
+     * <code>Set</code> of most-trusted CAs. Each element of the set is a
+     * {@link TrustAnchor TrustAnchor}. <p/> Note that the <code>Set</code>
+     * is copied to protect against subsequent modifications.
+     * 
+     * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s
+     * @throws InvalidAlgorithmParameterException if the specified
+     *             <code>Set</code> is empty.
+     * @throws NullPointerException if the specified <code>Set</code> is
+     *             <code>null</code>
+     * @throws ClassCastException if any of the elements in the <code>Set</code>
+     *             is not of type <code>java.security.cert.TrustAnchor</code>
+     */
+    public ExtendedPKIXParameters(Set trustAnchors)
+        throws InvalidAlgorithmParameterException
+    {
+        super(trustAnchors);
+        stores = new ArrayList();
+        additionalStores = new ArrayList();
+        trustedACIssuers = new HashSet();
+        necessaryACAttributes = new HashSet();
+        prohibitedACAttributes = new HashSet();
+        attrCertCheckers = new HashSet();
+    }
+
+    /**
+     * Returns an instance with the parameters of a given
+     * <code>PKIXParameters</code> object.
+     * 
+     * @param pkixParams The given <code>PKIXParameters</code>
+     * @return an extended PKIX params object
+     */
+    public static ExtendedPKIXParameters getInstance(PKIXParameters pkixParams)
+    {
+        ExtendedPKIXParameters params;
+        try
+        {
+            params = new ExtendedPKIXParameters(pkixParams.getTrustAnchors());
+        }
+        catch (Exception e)
+        {
+            // cannot happen
+            throw new RuntimeException(e.getMessage());
+        }
+        params.setParams(pkixParams);
+        return params;
+    }
+
+    /**
+     * Method to support <code>clone()</code> under J2ME.
+     * <code>super.clone()</code> does not exist and fields are not copied.
+     * 
+     * @param params Parameters to set. If this are
+     *            <code>ExtendedPKIXParameters</code> they are copied to.
+     */
+    protected void setParams(PKIXParameters params)
+    {
+        setDate(params.getDate());
+        setCertPathCheckers(params.getCertPathCheckers());
+        setCertStores(params.getCertStores());
+        setAnyPolicyInhibited(params.isAnyPolicyInhibited());
+        setExplicitPolicyRequired(params.isExplicitPolicyRequired());
+        setPolicyMappingInhibited(params.isPolicyMappingInhibited());
+        setRevocationEnabled(params.isRevocationEnabled());
+        setInitialPolicies(params.getInitialPolicies());
+        setPolicyQualifiersRejected(params.getPolicyQualifiersRejected());
+        setSigProvider(params.getSigProvider());
+        setTargetCertConstraints(params.getTargetCertConstraints());
+        try
+        {
+            setTrustAnchors(params.getTrustAnchors());
+        }
+        catch (Exception e)
+        {
+            // cannot happen
+            throw new RuntimeException(e.getMessage());
+        }
+        if (params instanceof ExtendedPKIXParameters)
+        {
+            ExtendedPKIXParameters _params = (ExtendedPKIXParameters) params;
+            validityModel = _params.validityModel;
+            useDeltas = _params.useDeltas;
+            additionalLocationsEnabled = _params.additionalLocationsEnabled;
+            selector = _params.selector == null ? null
+                : (Selector) _params.selector.clone();
+            stores = new ArrayList(_params.stores);
+            additionalStores = new ArrayList(_params.additionalStores);
+            trustedACIssuers = new HashSet(_params.trustedACIssuers);
+            prohibitedACAttributes = new HashSet(_params.prohibitedACAttributes);
+            necessaryACAttributes = new HashSet(_params.necessaryACAttributes);
+            attrCertCheckers = new HashSet(_params.attrCertCheckers);
+        }
+    }
+
+    /**
+     * This is the default PKIX validity model. Actually there are two variants
+     * of this: The PKIX model and the modified PKIX model. The PKIX model
+     * verifies that all involved certificates must have been valid at the
+     * current time. The modified PKIX model verifies that all involved
+     * certificates were valid at the signing time. Both are indirectly choosen
+     * with the {@link PKIXParameters#setDate(java.util.Date)} method, so this
+     * methods sets the Date when <em>all</em> certificates must have been
+     * valid.
+     */
+    public static final int PKIX_VALIDITY_MODEL = 0;
+
+    /**
+     * This model uses the following validity model. Each certificate must have
+     * been valid at the moment where is was used. That means the end
+     * certificate must have been valid at the time the signature was done. The
+     * CA certificate which signed the end certificate must have been valid,
+     * when the end certificate was signed. The CA (or Root CA) certificate must
+     * have been valid, when the CA certificate was signed and so on. So the
+     * {@link PKIXParameters#setDate(java.util.Date)} method sets the time, when
+     * the <em>end certificate</em> must have been valid. <p/> It is used e.g.
+     * in the German signature law.
+     */
+    public static final int CHAIN_VALIDITY_MODEL = 1;
+
+    private int validityModel = PKIX_VALIDITY_MODEL;
+
+    private boolean useDeltas = false;
+
+    /**
+     * Defaults to <code>false</code>.
+     * 
+     * @return Returns if delta CRLs should be used.
+     */
+    public boolean isUseDeltasEnabled()
+    {
+        return useDeltas;
+    }
+
+    /**
+     * Sets if delta CRLs should be used for checking the revocation status.
+     * 
+     * @param useDeltas <code>true</code> if delta CRLs should be used.
+     */
+    public void setUseDeltasEnabled(boolean useDeltas)
+    {
+        this.useDeltas = useDeltas;
+    }
+
+    /**
+     * @return Returns the validity model.
+     * @see #CHAIN_VALIDITY_MODEL
+     * @see #PKIX_VALIDITY_MODEL
+     */
+    public int getValidityModel()
+    {
+        return validityModel;
+    }
+
+    /**
+     * Sets the Java CertStore to this extended PKIX parameters.
+     * 
+     * @throws ClassCastException if an element of <code>stores</code> is not
+     *             a <code>CertStore</code>.
+     */
+    public void setCertStores(List stores)
+    {
+        if (stores != null)
+        {
+            Iterator it = stores.iterator();
+            while (it.hasNext())
+            {
+                addCertStore((CertStore)it.next());
+            }
+        }
+    }
+
+    /**
+     * Sets the Bouncy Castle Stores for finding CRLs, certificates, attribute
+     * certificates or cross certificates.
+     * <p>
+     * The <code>List</code> is cloned.
+     * 
+     * @param stores A list of stores to use.
+     * @see #getStores
+     * @throws ClassCastException if an element of <code>stores</code> is not
+     *             a {@link Store}.
+     */
+    public void setStores(List stores)
+    {
+        if (stores == null)
+        {
+            this.stores = new ArrayList();
+        }
+        else
+        {
+            for (Iterator i = stores.iterator(); i.hasNext();)
+            {
+                if (!(i.next() instanceof Store))
+                {
+                    throw new ClassCastException(
+                        "All elements of list must be "
+                            + "of type org.bouncycastle.util.Store.");
+                }
+            }
+            this.stores = new ArrayList(stores);
+        }
+    }
+
+    /**
+     * Adds a Bouncy Castle {@link Store} to find CRLs, certificates, attribute
+     * certificates or cross certificates.
+     * <p>
+     * This method should be used to add local stores, like collection based
+     * X.509 stores, if available. Local stores should be considered first,
+     * before trying to use additional (remote) locations, because they do not
+     * need possible additional network traffic.
+     * <p>
+     * If <code>store</code> is <code>null</code> it is ignored.
+     * 
+     * @param store The store to add.
+     * @see #getStores
+     */
+    public void addStore(Store store)
+    {
+        if (store != null)
+        {
+            stores.add(store);
+        }
+    }
+
+    /**
+     * Adds an additional Bouncy Castle {@link Store} to find CRLs, certificates,
+     * attribute certificates or cross certificates.
+     * <p>
+     * You should not use this method. This method is used for adding additional
+     * X.509 stores, which are used to add (remote) locations, e.g. LDAP, found
+     * during X.509 object processing, e.g. in certificates or CRLs. This method
+     * is used in PKIX certification path processing.
+     * <p>
+     * If <code>store</code> is <code>null</code> it is ignored.
+     * 
+     * @param store The store to add.
+     * @see #getStores()
+     */
+    public void addAdditionalStore(Store store)
+    {
+        if (store != null)
+        {
+            additionalStores.add(store);
+        }
+    }
+
+    /**
+     * @deprecated
+     */
+    public void addAddionalStore(Store store)
+    {
+        addAdditionalStore(store);      
+    }
+
+    /**
+     * Returns an immutable <code>List</code> of additional Bouncy Castle
+     * <code>Store</code>s used for finding CRLs, certificates, attribute
+     * certificates or cross certificates.
+     * 
+     * @return an immutable <code>List</code> of additional Bouncy Castle
+     *         <code>Store</code>s. Never <code>null</code>.
+     * 
+     * @see #addAdditionalStore(Store)
+     */
+    public List getAdditionalStores()
+    {
+        return Collections.unmodifiableList(additionalStores);
+    }
+
+    /**
+     * Returns an immutable <code>List</code> of Bouncy Castle
+     * <code>Store</code>s used for finding CRLs, certificates, attribute
+     * certificates or cross certificates.
+     * 
+     * @return an immutable <code>List</code> of Bouncy Castle
+     *         <code>Store</code>s. Never <code>null</code>.
+     * 
+     * @see #setStores(List)
+     */
+    public List getStores()
+    {
+        return Collections.unmodifiableList(new ArrayList(stores));
+    }
+
+    /**
+     * @param validityModel The validity model to set.
+     * @see #CHAIN_VALIDITY_MODEL
+     * @see #PKIX_VALIDITY_MODEL
+     */
+    public void setValidityModel(int validityModel)
+    {
+        this.validityModel = validityModel;
+    }
+
+    public Object clone()
+    {
+        ExtendedPKIXParameters params;
+        try
+        {
+            params = new ExtendedPKIXParameters(getTrustAnchors());
+        }
+        catch (Exception e)
+        {
+            // cannot happen
+            throw new RuntimeException(e.getMessage());
+        }
+        params.setParams(this);
+        return params;
+    }
+
+    /**
+     * Returns if additional {@link X509Store}s for locations like LDAP found
+     * in certificates or CRLs should be used.
+     * 
+     * @return Returns <code>true</code> if additional stores are used.
+     */
+    public boolean isAdditionalLocationsEnabled()
+    {
+        return additionalLocationsEnabled;
+    }
+
+    /**
+     * Sets if additional {@link X509Store}s for locations like LDAP found in
+     * certificates or CRLs should be used.
+     * 
+     * @param enabled <code>true</code> if additional stores are used.
+     */
+    public void setAdditionalLocationsEnabled(boolean enabled)
+    {
+        additionalLocationsEnabled = enabled;
+    }
+
+    /**
+     * Returns the required constraints on the target certificate or attribute
+     * certificate. The constraints are returned as an instance of
+     * <code>Selector</code>. If <code>null</code>, no constraints are
+     * defined.
+     * 
+     * <p>
+     * The target certificate in a PKIX path may be a certificate or an
+     * attribute certificate.
+     * <p>
+     * Note that the <code>Selector</code> returned is cloned to protect
+     * against subsequent modifications.
+     * 
+     * @return a <code>Selector</code> specifying the constraints on the
+     *         target certificate or attribute certificate (or <code>null</code>)
+     * @see #setTargetConstraints
+     * @see X509CertStoreSelector
+     * @see X509AttributeCertStoreSelector
+     */
+    public Selector getTargetConstraints()
+    {
+        if (selector != null)
+        {
+            return (Selector) selector.clone();
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the required constraints on the target certificate or attribute
+     * certificate. The constraints are specified as an instance of
+     * <code>Selector</code>. If <code>null</code>, no constraints are
+     * defined.
+     * <p>
+     * The target certificate in a PKIX path may be a certificate or an
+     * attribute certificate.
+     * <p>
+     * Note that the <code>Selector</code> specified is cloned to protect
+     * against subsequent modifications.
+     * 
+     * @param selector a <code>Selector</code> specifying the constraints on
+     *            the target certificate or attribute certificate (or
+     *            <code>null</code>)
+     * @see #getTargetConstraints
+     * @see X509CertStoreSelector
+     * @see X509AttributeCertStoreSelector
+     */
+    public void setTargetConstraints(Selector selector)
+    {
+        if (selector != null)
+        {
+            this.selector = (Selector) selector.clone();
+        }
+        else
+        {
+            this.selector = null;
+        }
+    }
+
+    /**
+     * Sets the required constraints on the target certificate. The constraints
+     * are specified as an instance of <code>X509CertSelector</code>. If
+     * <code>null</code>, no constraints are defined.
+     * 
+     * <p>
+     * This method wraps the given <code>X509CertSelector</code> into a
+     * <code>X509CertStoreSelector</code>.
+     * <p>
+     * Note that the <code>X509CertSelector</code> specified is cloned to
+     * protect against subsequent modifications.
+     * 
+     * @param selector a <code>X509CertSelector</code> specifying the
+     *            constraints on the target certificate (or <code>null</code>)
+     * @see #getTargetCertConstraints
+     * @see X509CertStoreSelector
+     */
+    public void setTargetCertConstraints(CertSelector selector)
+    {
+        super.setTargetCertConstraints(selector);
+        if (selector != null)
+        {
+            this.selector = X509CertStoreSelector
+                .getInstance((X509CertSelector) selector);
+        }
+        else
+        {
+            this.selector = null;
+        }
+    }
+
+    /**
+     * Returns the trusted attribute certificate issuers. If attribute
+     * certificates is verified the trusted AC issuers must be set.
+     * <p>
+     * The returned <code>Set</code> consists of <code>TrustAnchor</code>s.
+     * <p>
+     * The returned <code>Set</code> is immutable. Never <code>null</code>
+     * 
+     * @return Returns an immutable set of the trusted AC issuers.
+     */
+    public Set getTrustedACIssuers()
+    {
+        return Collections.unmodifiableSet(trustedACIssuers);
+    }
+
+    /**
+     * Sets the trusted attribute certificate issuers. If attribute certificates
+     * is verified the trusted AC issuers must be set.
+     * <p>
+     * The <code>trustedACIssuers</code> must be a <code>Set</code> of
+     * <code>TrustAnchor</code>
+     * <p>
+     * The given set is cloned.
+     * 
+     * @param trustedACIssuers The trusted AC issuers to set. Is never
+     *            <code>null</code>.
+     * @throws ClassCastException if an element of <code>stores</code> is not
+     *             a <code>TrustAnchor</code>.
+     */
+    public void setTrustedACIssuers(Set trustedACIssuers)
+    {
+        if (trustedACIssuers == null)
+        {
+            this.trustedACIssuers.clear();
+            return;
+        }
+        for (Iterator it = trustedACIssuers.iterator(); it.hasNext();)
+        {
+            if (!(it.next() instanceof TrustAnchor))
+            {
+                throw new ClassCastException("All elements of set must be "
+                    + "of type " + TrustAnchor.class.getName() + ".");
+            }
+        }
+        this.trustedACIssuers.clear();
+        this.trustedACIssuers.addAll(trustedACIssuers);
+    }
+
+    /**
+     * Returns the neccessary attributes which must be contained in an attribute
+     * certificate.
+     * <p>
+     * The returned <code>Set</code> is immutable and contains
+     * <code>String</code>s with the OIDs.
+     * 
+     * @return Returns the necessary AC attributes.
+     */
+    public Set getNecessaryACAttributes()
+    {
+        return Collections.unmodifiableSet(necessaryACAttributes);
+    }
+
+    /**
+     * Sets the neccessary which must be contained in an attribute certificate.
+     * <p>
+     * The <code>Set</code> must contain <code>String</code>s with the
+     * OIDs.
+     * <p>
+     * The set is cloned.
+     * 
+     * @param necessaryACAttributes The necessary AC attributes to set.
+     * @throws ClassCastException if an element of
+     *             <code>necessaryACAttributes</code> is not a
+     *             <code>String</code>.
+     */
+    public void setNecessaryACAttributes(Set necessaryACAttributes)
+    {
+        if (necessaryACAttributes == null)
+        {
+            this.necessaryACAttributes.clear();
+            return;
+        }
+        for (Iterator it = necessaryACAttributes.iterator(); it.hasNext();)
+        {
+            if (!(it.next() instanceof String))
+            {
+                throw new ClassCastException("All elements of set must be "
+                    + "of type String.");
+            }
+        }
+        this.necessaryACAttributes.clear();
+        this.necessaryACAttributes.addAll(necessaryACAttributes);
+    }
+
+    /**
+     * Returns the attribute certificates which are not allowed.
+     * <p>
+     * The returned <code>Set</code> is immutable and contains
+     * <code>String</code>s with the OIDs.
+     * 
+     * @return Returns the prohibited AC attributes. Is never <code>null</code>.
+     */
+    public Set getProhibitedACAttributes()
+    {
+        return Collections.unmodifiableSet(prohibitedACAttributes);
+    }
+
+    /**
+     * Sets the attribute certificates which are not allowed.
+     * <p>
+     * The <code>Set</code> must contain <code>String</code>s with the
+     * OIDs.
+     * <p>
+     * The set is cloned.
+     * 
+     * @param prohibitedACAttributes The prohibited AC attributes to set.
+     * @throws ClassCastException if an element of
+     *             <code>prohibitedACAttributes</code> is not a
+     *             <code>String</code>.
+     */
+    public void setProhibitedACAttributes(Set prohibitedACAttributes)
+    {
+        if (prohibitedACAttributes == null)
+        {
+            this.prohibitedACAttributes.clear();
+            return;
+        }
+        for (Iterator it = prohibitedACAttributes.iterator(); it.hasNext();)
+        {
+            if (!(it.next() instanceof String))
+            {
+                throw new ClassCastException("All elements of set must be "
+                    + "of type String.");
+            }
+        }
+        this.prohibitedACAttributes.clear();
+        this.prohibitedACAttributes.addAll(prohibitedACAttributes);
+    }
+
+    /**
+     * Returns the attribute certificate checker. The returned set contains
+     * {@link PKIXAttrCertChecker}s and is immutable.
+     * 
+     * @return Returns the attribute certificate checker. Is never
+     *         <code>null</code>.
+     */
+    public Set getAttrCertCheckers()
+    {
+        return Collections.unmodifiableSet(attrCertCheckers);
+    }
+
+    /**
+     * Sets the attribute certificate checkers.
+     * <p>
+     * All elements in the <code>Set</code> must a {@link PKIXAttrCertChecker}.
+     * <p>
+     * The given set is cloned.
+     * 
+     * @param attrCertCheckers The attribute certificate checkers to set. Is
+     *            never <code>null</code>.
+     * @throws ClassCastException if an element of <code>attrCertCheckers</code>
+     *             is not a <code>PKIXAttrCertChecker</code>.
+     */
+    public void setAttrCertCheckers(Set attrCertCheckers)
+    {
+        if (attrCertCheckers == null)
+        {
+            this.attrCertCheckers.clear();
+            return;
+        }
+        for (Iterator it = attrCertCheckers.iterator(); it.hasNext();)
+        {
+            if (!(it.next() instanceof PKIXAttrCertChecker))
+            {
+                throw new ClassCastException("All elements of set must be "
+                    + "of type " + PKIXAttrCertChecker.class.getName() + ".");
+            }
+        }
+        this.attrCertCheckers.clear();
+        this.attrCertCheckers.addAll(attrCertCheckers);
+    }
+
+}
diff --git a/src/main/java/org/bouncycastle/x509/NoSuchStoreException.java b/src/main/java/org/bouncycastle/x509/NoSuchStoreException.java
new file mode 100644
index 0000000..255c030
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/NoSuchStoreException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.x509;
+
+public class NoSuchStoreException
+    extends Exception
+{
+    public NoSuchStoreException(String message)
+    {
+        super(message);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/PKIXAttrCertChecker.java b/src/main/java/org/bouncycastle/x509/PKIXAttrCertChecker.java
new file mode 100644
index 0000000..816cdab
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/PKIXAttrCertChecker.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.x509;
+
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidatorException;
+import java.util.Collection;
+import java.util.Set;
+
+public abstract class PKIXAttrCertChecker
+    implements Cloneable
+{
+
+    /**
+     * Returns an immutable <code>Set</code> of X.509 attribute certificate
+     * extensions that this <code>PKIXAttrCertChecker</code> supports or
+     * <code>null</code> if no extensions are supported.
+     * <p>
+     * Each element of the set is a <code>String</code> representing the
+     * Object Identifier (OID) of the X.509 extension that is supported.
+     * <p>
+     * All X.509 attribute certificate extensions that a
+     * <code>PKIXAttrCertChecker</code> might possibly be able to process
+     * should be included in the set.
+     * 
+     * @return an immutable <code>Set</code> of X.509 extension OIDs (in
+     *         <code>String</code> format) supported by this
+     *         <code>PKIXAttrCertChecker</code>, or <code>null</code> if no
+     *         extensions are supported
+     */
+    public abstract Set getSupportedExtensions();
+
+    /**
+     * Performs checks on the specified attribute certificate. Every handled
+     * extension is rmeoved from the <code>unresolvedCritExts</code>
+     * collection.
+     * 
+     * @param attrCert The attribute certificate to be checked.
+     * @param certPath The certificate path which belongs to the attribute
+     *            certificate issuer public key certificate.
+     * @param holderCertPath The certificate path which belongs to the holder
+     *            certificate.
+     * @param unresolvedCritExts a <code>Collection</code> of OID strings
+     *            representing the current set of unresolved critical extensions
+     * @throws CertPathValidatorException if the specified attribute certificate
+     *             does not pass the check.
+     */
+    public abstract void check(X509AttributeCertificate attrCert, CertPath certPath,
+                                 CertPath holderCertPath, Collection unresolvedCritExts)
+        throws CertPathValidatorException;
+
+    /**
+     * Returns a clone of this object.
+     * 
+     * @return a copy of this <code>PKIXAttrCertChecker</code>
+     */
+    public abstract Object clone();
+}
diff --git a/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java b/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java
deleted file mode 100644
index cc00697..0000000
--- a/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java
+++ /dev/null
@@ -1,2437 +0,0 @@
-package org.bouncycastle.x509;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.cert.CertPath;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.PKIXCertPathChecker;
-import java.security.cert.PKIXParameters;
-import java.security.cert.PolicyNode;
-import java.security.cert.TrustAnchor;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLEntry;
-import java.security.cert.X509CRLSelector;
-import java.security.cert.X509CertSelector;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DEREnumerated;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObject;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.x509.AccessDescription;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.CRLDistPoint;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.GeneralSubtree;
-import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
-import org.bouncycastle.asn1.x509.NameConstraints;
-import org.bouncycastle.asn1.x509.PolicyInformation;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.asn1.x509.qualified.Iso4217CurrencyCode;
-import org.bouncycastle.asn1.x509.qualified.MonetaryValue;
-import org.bouncycastle.asn1.x509.qualified.QCStatement;
-import org.bouncycastle.i18n.ErrorBundle;
-import org.bouncycastle.i18n.filter.UntrustedInput;
-import org.bouncycastle.jce.provider.CertPathValidatorUtilities;
-import org.bouncycastle.jce.provider.PKIXPolicyNode;
-import org.bouncycastle.jce.provider.AnnotatedException;
-
-/**
- * PKIXCertPathReviewer<br>
- * Validation of X.509 Certificate Paths. Tries to find as much errors in the Path as possible.
- */
-public class PKIXCertPathReviewer extends CertPathValidatorUtilities
-{
-    
-    private static final String QC_STATEMENT = X509Extensions.QCStatements.getId();
-    private static final String CRL_DIST_POINTS = X509Extensions.CRLDistributionPoints.getId();
-    private static final String AUTH_INFO_ACCESS = X509Extensions.AuthorityInfoAccess.getId();
-    
-    private static final String RESOURCE_NAME = "org.bouncycastle.x509.CertPathReviewerMessages";
-    
-    // input parameters
-    
-    protected CertPath certPath;
-
-    protected PKIXParameters pkixParams;
-
-    protected Date validDate;
-
-    // state variables
-    
-    protected List certs;
-
-    protected int n;
-    
-    // output variables
-    
-    protected List[] notifications;
-    protected List[] errors;
-    protected TrustAnchor trustAnchor;
-    protected PublicKey subjectPublicKey;
-    protected PolicyNode policyTree;
-    
-    
-    /**
-     * Creates a PKIXCertPathReviewer for the given {@link CertPath} and {@link PKIXParameters} params
-     * @param certPath the {@link CertPath} to validate
-     * @param params the {@link PKIXParameters} to use
-     * @throws CertPathReviewerException if the certPath is empty
-     */
-    public PKIXCertPathReviewer(CertPath certPath, PKIXParameters params)
-            throws CertPathReviewerException
-    {
-        // check input parameters
-        if (certPath == null)
-        {
-            throw new NullPointerException("certPath was null");
-        }
-        this.certPath = certPath;
-
-        certs = certPath.getCertificates();
-        n = certs.size();
-        if (certs.isEmpty())
-        {
-            throw new CertPathReviewerException(
-                    new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.emptyCertPath"));
-        }
-
-        pkixParams = (PKIXParameters) params.clone();
-
-        // 6.1.1 - Inputs
-
-        // a) done
-
-        // b)
-
-        validDate = getValidDate(pkixParams);
-
-        // c) part of pkixParams
-
-        // d) done at the beginning of checkSignatures
-
-        // e) f) g) part of pkixParams
-        
-        // initialize output parameters
-        
-        notifications = null;
-        errors = null;
-        trustAnchor = null;
-        subjectPublicKey = null;
-        policyTree = null;
-
-    }
-    
-    /**
-     * 
-     * @return the CertPath that was validated
-     */
-    public CertPath getCertPath()
-    {
-        return certPath;
-    }
-    
-    /**
-     * 
-     * @return the size of the CertPath
-     */
-    public int getCertPathSize()
-    {
-        return n;
-    }
-
-    /**
-     * Returns an Array of Lists which contains a List of global error messages 
-     * and a List of error messages for each certificate in the path.
-     * The global error List is at index 0. The error lists for each certificate at index 1 to n. 
-     * The error messages are of type.
-     * @return the Array of Lists which contain the error messages
-     */
-    public List[] getErrors()
-    {
-        doChecks();
-        return errors;
-    }
-    
-    /**
-     * Returns an List of error messages for the certificate at the given index in the CertPath.
-     * If index == -1 then the list of global errors is returned with errors not specific to a certificate. 
-     * @param index the index of the certificate in the CertPath
-     * @return List of error messages for the certificate
-     */
-    public List getErrors(int index)
-    {
-        doChecks();
-        return errors[index + 1];
-    }
-
-    /**
-     * Returns an Array of Lists which contains a List of global notification messages 
-     * and a List of botification messages for each certificate in the path.
-     * The global notificatio List is at index 0. The notification lists for each certificate at index 1 to n. 
-     * The error messages are of type.
-     * @return the Array of Lists which contain the notification messages
-     */
-    public List[] getNotifications()
-    {
-        doChecks();
-        return notifications;
-    }
-    
-    /**
-     * Returns an List of notification messages for the certificate at the given index in the CertPath.
-     * If index == -1 then the list of global notifications is returned with notifications not specific to a certificate. 
-     * @param index the index of the certificate in the CertPath
-     * @return List of notification messages for the certificate
-     */
-    public List getNotifications(int index)
-    {
-        doChecks();
-        return notifications[index + 1];
-    }
-
-    /**
-     * 
-     * @return the valid policy tree, <b>null</b> if no valid policy exists.
-     */
-    public PolicyNode getPolicyTree()
-    {
-        doChecks();
-        return policyTree;
-    }
-
-    /**
-     * 
-     * @return the PublicKey if the last certificate in the CertPath
-     */
-    public PublicKey getSubjectPublicKey()
-    {
-        doChecks();
-        return subjectPublicKey;
-    }
-
-    /**
-     * 
-     * @return the TrustAnchor for the CertPath, <b>null</b> if no valid TrustAnchor was found.
-     */
-    public TrustAnchor getTrustAnchor()
-    {
-        doChecks();
-        return trustAnchor;
-    }
-    
-    /**
-     * 
-     * @return if the CertPath is valid
-     */
-    public boolean isValidCertPath()
-    {
-        doChecks();
-        boolean valid = true;
-        for (int i = 0; i < errors.length; i++)
-        {
-            if (!errors[i].isEmpty())
-            {
-                valid = false;
-                break;
-            }
-        }
-        return valid;
-    }
-    
-    protected void addNotification(ErrorBundle msg)
-    {
-        notifications[0].add(msg);
-    }
-    
-    protected void addNotification(ErrorBundle msg, int index)
-    {
-        if (index < -1 || index >= n)
-        {
-            throw new IndexOutOfBoundsException();
-        }
-        notifications[index + 1].add(msg);
-    }
-
-    protected void addError(ErrorBundle msg) 
-    {
-        errors[0].add(msg);
-    }
-    
-    protected void addError(ErrorBundle msg, int index)
-    {
-        if (index < -1 || index >= n)
-        {
-            throw new IndexOutOfBoundsException();
-        }
-        errors[index + 1].add(msg);
-    }
-    
-    protected void doChecks()
-    {
-        if (notifications == null)
-        {
-            // initialize lists
-            notifications = new List[n+1];
-            errors = new List[n+1];
-            
-            for (int i = 0; i < notifications.length; i++)
-            {
-                notifications[i] = new ArrayList();
-                errors[i] = new ArrayList();
-            }
-            
-            // check Signatures
-            checkSignatures();
-            
-            // check Name Constraints
-            checkNameConstraints();
-            
-            // check Path Length
-            checkPathLength();
-            
-            // check Policy
-            checkPolicy();
-            
-            // check other critical extensions
-            checkCriticalExtensions();
-            
-        }
-    }
-
-    private void checkNameConstraints()
-    {
-        X509Certificate cert = null;
-        
-        //
-        // Setup
-        //
-        
-        // (b)
-        Set     permittedSubtreesDN = new HashSet();
-        Set     permittedSubtreesEmail = new HashSet();
-        Set     permittedSubtreesIP = new HashSet();
-    
-        // (c)
-        Set     excludedSubtreesDN = new HashSet();
-        Set     excludedSubtreesEmail = new HashSet();
-        Set     excludedSubtreesIP = new HashSet();
-
-        //
-        // process each certificate except the last in the path
-        //
-        int index;
-        int i;
-        
-        try 
-        {
-            for (index = certs.size()-1; index>0; index--) 
-            {
-                i = n - index;
-                
-                //
-                // certificate processing
-                //    
-                
-                cert = (X509Certificate) certs.get(index);
-                
-                // b),c)
-                
-                if (!isSelfIssued(cert))
-                {
-                    X500Principal principal = getSubjectPrincipal(cert);
-                    ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(principal.getEncoded()));
-                    ASN1Sequence    dns;
-    
-                    try
-                    {
-                        dns = (ASN1Sequence)aIn.readObject();
-                    }
-                    catch (IOException e)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.ncSubjectNameError", 
-                                new Object[] {new UntrustedInput(principal)});
-                        throw new CertPathReviewerException(msg,e,certPath,index);
-                    }
-    
-                    try
-                    {
-                        checkPermittedDN(permittedSubtreesDN, dns);
-                    }
-                    catch (CertPathValidatorException cpve)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.notPermittedDN", 
-                                new Object[] {new UntrustedInput(principal.getName())});
-                        throw new CertPathReviewerException(msg,cpve,certPath,index);
-                    }
-                    
-                    try
-                    {
-                        checkExcludedDN(excludedSubtreesDN, dns);
-                    }
-                    catch (CertPathValidatorException cpve)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.excludedDN",
-                                new Object[] {new UntrustedInput(principal.getName())});
-                        throw new CertPathReviewerException(msg,cpve,certPath,index);
-                    }
-            
-                    ASN1Sequence altName;
-                    try 
-                    {
-                        altName = (ASN1Sequence)getExtensionValue(cert, SUBJECT_ALTERNATIVE_NAME);
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.subjAltNameExtError");
-                        throw new CertPathReviewerException(msg,ae,certPath,index);
-                    }
-                    
-                    if (altName != null)
-                    {
-                        for (int j = 0; j < altName.size(); j++)
-                        {
-                            ASN1TaggedObject o = (ASN1TaggedObject)altName.getObjectAt(j);
-    
-                            switch(o.getTagNo())
-                            {
-                            case 1:
-                                String email = DERIA5String.getInstance(o, true).getString();
-    
-                                try
-                                {
-                                    checkPermittedEmail(permittedSubtreesEmail, email);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.notPermittedEmail", 
-                                            new Object[] {new UntrustedInput(email)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                
-                                try
-                                {
-                                    checkExcludedEmail(excludedSubtreesEmail, email);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.excludedEmail", 
-                                            new Object[] {new UntrustedInput(email)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-
-                                break;
-                            case 4:
-                                ASN1Sequence altDN = ASN1Sequence.getInstance(o, true);
-    
-                                try
-                                {
-                                    checkPermittedDN(permittedSubtreesDN, altDN);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    X509Name altDNName = new X509Name(altDN);
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.notPermittedDN", 
-                                            new Object[] {new UntrustedInput(altDNName)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                
-                                try
-                                {
-                                    checkExcludedDN(excludedSubtreesDN, altDN);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    X509Name altDNName = new X509Name(altDN);
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.excludedDN", 
-                                            new Object[] {new UntrustedInput(altDNName)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                
-                                break;
-                            case 7:
-                                byte[] ip = ASN1OctetString.getInstance(o, true).getOctets();
-    
-                                try
-                                {
-                                    checkPermittedIP(permittedSubtreesIP, ip);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.notPermittedIP", 
-                                            new Object[] {IPtoString(ip)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                
-                                try
-                                {
-                                    checkExcludedIP(excludedSubtreesIP, ip);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.excludedIP", 
-                                            new Object[] {IPtoString(ip)});
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                            }
-                        }
-                    }
-                }
-                
-                //
-                // prepare for next certificate
-                //
-                
-                //
-                // (g) handle the name constraints extension
-                //
-                ASN1Sequence ncSeq;
-                try 
-                {
-                    ncSeq = (ASN1Sequence)getExtensionValue(cert, NAME_CONSTRAINTS);
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.ncExtError");
-                    throw new CertPathReviewerException(msg,ae,certPath,index);
-                }
-                
-                if (ncSeq != null)
-                {
-                    NameConstraints nc = new NameConstraints(ncSeq);
-
-                    //
-                    // (g) (1) permitted subtrees
-                    //
-                    ASN1Sequence permitted = nc.getPermittedSubtrees();
-                    if (permitted != null)
-                    {
-                        Enumeration e = permitted.getObjects();
-                        while (e.hasMoreElements())
-                        {
-                            GeneralSubtree  subtree = GeneralSubtree.getInstance(e.nextElement());
-                            GeneralName     base = subtree.getBase();
-
-                            switch(base.getTagNo())
-                            {
-                                case 1:
-                                    permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString());
-                                    break;
-                                case 4:
-                                    permittedSubtreesDN = intersectDN(permittedSubtreesDN, (ASN1Sequence)base.getName());
-                                    break;
-                                case 7:
-                                    permittedSubtreesIP = intersectIP(permittedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets());
-                                    break;
-                            }
-                        }
-                    }
-                
-                    //
-                    // (g) (2) excluded subtrees
-                    //
-                    ASN1Sequence excluded = nc.getExcludedSubtrees();
-                    if (excluded != null)
-                    {
-                        Enumeration e = excluded.getObjects();
-                        while (e.hasMoreElements())
-                        {
-                            GeneralSubtree  subtree = GeneralSubtree.getInstance(e.nextElement());
-                            GeneralName     base = subtree.getBase();
-
-                            switch(base.getTagNo())
-                            {
-                            case 1:
-                                excludedSubtreesEmail = unionEmail(excludedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString());
-                                break;
-                            case 4:
-                                excludedSubtreesDN = unionDN(excludedSubtreesDN, (ASN1Sequence)base.getName());
-                                break;
-                            case 7:
-                                excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets());
-                                break;
-                            }
-                        }
-                    }
-                }
-                
-            } // for
-        }
-        catch (CertPathReviewerException cpre)
-        {
-            addError(cpre.getErrorMessage(),cpre.getIndex());
-        }
-        
-    }
-
-    /*
-     * checks: - path length constraints and reports - total path length
-     */
-    private void checkPathLength()
-    {
-        // init
-        int maxPathLength = n;
-        int totalPathLength = 0;
-
-        X509Certificate cert = null;
-
-        int i;
-        for (int index = certs.size() - 1; index > 0; index--)
-        {
-            i = n - index;
-
-            cert = (X509Certificate) certs.get(index);
-
-            // l)
-
-            if (!isSelfIssued(cert))
-            {
-                if (maxPathLength <= 0)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.pathLenghtExtended");
-                    addError(msg);
-                }
-                maxPathLength--;
-                totalPathLength++;
-            }
-
-            // m)
-
-            BasicConstraints bc;
-            try
-            {
-                bc = BasicConstraints.getInstance(getExtensionValue(cert,
-                        BASIC_CONSTRAINTS));
-            }
-            catch (AnnotatedException ae)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.processLengthConstError");
-                addError(msg,index);
-                bc = null;
-            }
-
-            if (bc != null)
-            {
-                BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
-
-                if (_pathLengthConstraint != null)
-                {
-                    int _plc = _pathLengthConstraint.intValue();
-
-                    if (_plc < maxPathLength)
-                    {
-                        maxPathLength = _plc;
-                    }
-                }
-            }
-
-        }
-
-        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.totalPathLength",
-                new Object[] {Integer.valueOf(totalPathLength)});
-        
-        addNotification(msg);
-    }
-
-    /*
-     * checks: - signatures - name chaining - validity of certificates - todo:
-     * if certificate revoked (if specified in the parameters)
-     */
-    private void checkSignatures()
-    {
-        // 1.6.1 - Inputs
-        
-        // d)
-        
-        TrustAnchor trust = null;
-        X500Principal trustPrincipal = null;
-        
-        // validation date
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certPathValidDate",
-                    new Object[] {validDate, new Date()});
-            addNotification(msg);
-        }
-        
-        // find trust anchors
-        try
-        {
-            X509Certificate cert = (X509Certificate) certs.get(certs.size() - 1);
-            Collection trustColl = getTrustAnchors(cert,pkixParams.getTrustAnchors());
-            if (trustColl.size() > 1)
-            {
-                // conflicting trust anchors                
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                        "CertPathReviewer.conflictingTrustAnchors",
-                        new Object[] {Integer.valueOf(trustColl.size()),
-                                      new UntrustedInput(cert.getIssuerX500Principal())});
-                addError(msg);
-            }
-            else if (trustColl.isEmpty())
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                        "CertPathReviewer.noTrustAnchorFound",
-                        new Object[] {new UntrustedInput(cert.getIssuerX500Principal()),
-                                Integer.valueOf(pkixParams.getTrustAnchors().size())});
-                addError(msg);
-            }
-            else
-            {
-                PublicKey trustPublicKey;
-                trust = (TrustAnchor) trustColl.iterator().next();
-                if (trust.getTrustedCert() != null)
-                {
-                    trustPublicKey = trust.getTrustedCert().getPublicKey();
-                }
-                else
-                {
-                    trustPublicKey = trust.getCAPublicKey();
-                }
-                try
-                {
-                    cert.verify(trustPublicKey);
-                }
-                catch (Exception e)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.trustButInvalidCert");
-                    addError(msg);
-                    trust = null;
-                }
-            }
-        }
-        catch (CertPathReviewerException cpre)
-        {
-            addError(cpre.getErrorMessage());
-        }
-        
-        if (trust != null)
-        {
-            // get the name of the trustAnchor
-            X509Certificate sign = trust.getTrustedCert();
-            try
-            {
-                if (sign != null)
-                {
-                    trustPrincipal = getSubjectPrincipal(sign);
-                }
-                else
-                {
-                    trustPrincipal = new X500Principal(trust.getCAName());
-                }
-            }
-            catch (IllegalArgumentException ex)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.trustDNInvalid",
-                        new Object[] {new UntrustedInput(trust.getCAName())});
-                addError(msg);
-            }
-        }
-        
-        // 1.6.2 - Initialization
-        
-        PublicKey workingPublicKey = null;
-        X500Principal workingIssuerName = trustPrincipal;
-        
-        X509Certificate sign = null;
-
-        AlgorithmIdentifier workingAlgId = null;
-        DERObjectIdentifier workingPublicKeyAlgorithm = null;
-        DEREncodable workingPublicKeyParameters = null;
-        
-        if (trust != null)
-        {
-            sign = trust.getTrustedCert();
-            
-            if (sign != null)
-            {
-                workingPublicKey = sign.getPublicKey();
-            }
-            else
-            {
-                workingPublicKey = trust.getCAPublicKey();
-            }
-        
-            try
-            {
-                workingAlgId = getAlgorithmIdentifier(workingPublicKey);
-                workingPublicKeyAlgorithm = workingAlgId.getObjectId();
-                workingPublicKeyParameters = workingAlgId.getParameters();
-            }
-            catch (CertPathValidatorException ex)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.trustPubKeyError");
-                addError(msg);
-                workingAlgId = null;
-            }
-            
-        }
-
-        // Basic cert checks
-
-        X509Certificate cert = null;
-        int i;
-
-        for (int index = certs.size() - 1; index >= 0; index--)
-        {
-            //
-            // i as defined in the algorithm description
-            //
-            i = n - index;
-
-            //
-            // set certificate to be checked in this round
-            // sign and workingPublicKey and workingIssuerName are set
-            // at the end of the for loop and initialied the
-            // first time from the TrustAnchor
-            //
-            cert = (X509Certificate) certs.get(index);
-
-            // verify signature
-            if (workingPublicKey != null)
-            {
-                try
-                {
-                    cert.verify(workingPublicKey, "BC");
-                }
-                catch (GeneralSecurityException ex)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.signatureNotVerified",
-                            new Object[] {ex.getMessage(),ex}); 
-                    addError(msg,index);
-                }
-            }
-            else
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.NoIssuerPublicKey");
-                addError(msg,index);
-            }
-
-            // certificate valid?
-            try
-            {
-                cert.checkValidity(validDate);
-            }
-            catch (CertificateNotYetValidException cnve)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certificateNotYetValid",
-                        new Object[] {cert.getNotBefore()});
-                addError(msg,index);
-            }
-            catch (CertificateExpiredException cee)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certificateExpired",
-                        new Object[] {cert.getNotAfter()});
-                addError(msg,index);
-            }
-
-            // certificate revoked?
-            if (pkixParams.isRevocationEnabled())
-            {
-                // read crl distribution points extension
-                CRLDistPoint crlDistPoints = null;
-                try
-                {
-                    DERObject crl_dp = getExtensionValue(cert,CRL_DIST_POINTS);
-                    if (crl_dp != null)
-                    {
-                        crlDistPoints = CRLDistPoint.getInstance(crl_dp);
-                    }
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlDistPtExtError");
-                    addError(msg,index);
-                }
-
-                // read authority information access extension
-                AuthorityInformationAccess authInfoAcc = null;
-                try
-                {
-                    DERObject auth_info_acc = getExtensionValue(cert,AUTH_INFO_ACCESS);
-                    if (auth_info_acc != null)
-                    {
-                        authInfoAcc = AuthorityInformationAccess.getInstance(auth_info_acc);
-                    }
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlAuthInfoAccError");
-                    addError(msg,index);
-                }
-                
-                Vector crlDistPointUrls = getCRLDistUrls(crlDistPoints,authInfoAcc);
-                Vector ocspUrls = getOCSPUrls(authInfoAcc);
-                
-                // add notifications with the crl distribution points
-                
-                // output crl distribution points
-                Iterator urlIt = crlDistPointUrls.iterator();
-                while (urlIt.hasNext())
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlDistPoint",
-                                new Object[] {new UntrustedInput(urlIt.next())});
-                    addNotification(msg,index);
-                }
-                
-                // output ocsp urls
-                urlIt = ocspUrls.iterator();
-                while (urlIt.hasNext())
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.ocspLocation",
-                            new Object[] {new UntrustedInput(urlIt.next())});
-                    addNotification(msg,index);
-                }
-                
-                // TODO also support Netscapes revocation-url and/or OCSP instead of CRLs for revocation checking
-                // check CRLs
-                try 
-                {
-                    checkCRLs(pkixParams, cert, validDate, sign, workingPublicKey, crlDistPointUrls, index);
-                }
-                catch (CertPathReviewerException cpre)
-                {
-                    addError(cpre.getErrorMessage(),index);
-                }
-            }
-
-            // certificate issuer correct
-            if (workingIssuerName != null && !cert.getIssuerX500Principal().equals(workingIssuerName))
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certWrongIssuer",
-                            new Object[] {workingIssuerName.getName(),
-                            cert.getIssuerX500Principal().getName()});
-                addError(msg,index);
-            }
-
-            //
-            // prepare for next certificate
-            //
-            if (i != n)
-            {
-
-                if (cert != null && cert.getVersion() == 1)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noCACert");
-                    addError(msg,index);
-                }
-
-                // k)
-
-                BasicConstraints bc;
-                try
-                {
-                    bc = BasicConstraints.getInstance(getExtensionValue(cert,
-                            BASIC_CONSTRAINTS));
-                    if (bc != null)
-                    {
-                        if (!bc.isCA())
-                        {
-                            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noCACert");
-                            addError(msg,index);
-                        }
-                    }
-                    else
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noBasicConstraints");
-                        addError(msg,index);
-                    }
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.errorProcesingBC");
-                    addError(msg,index);
-                }
-
-                // n)
-
-                boolean[] _usage = cert.getKeyUsage();
-
-                if ((_usage != null) && !_usage[KEY_CERT_SIGN])
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noCertSign");
-                    addError(msg,index);
-                }
-
-            } // if
-
-            // set signing certificate for next round
-            sign = cert;
-            
-            // c)
-
-            workingIssuerName = cert.getSubjectX500Principal();
-
-            // d)
-
-            workingPublicKey = cert.getPublicKey();
-
-            // e) f)
-
-            try
-            {
-                workingAlgId = getAlgorithmIdentifier(workingPublicKey);
-                workingPublicKeyAlgorithm = workingAlgId.getObjectId();
-                workingPublicKeyParameters = workingAlgId.getParameters();
-            }
-            catch (CertPathValidatorException ex)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.pubKeyError");
-                addError(msg,index);
-                workingAlgId = null;
-                workingPublicKeyAlgorithm = null;
-                workingPublicKeyParameters = null;
-            }
-
-        } // for
-
-        trustAnchor = trust;
-        subjectPublicKey = workingPublicKey;
-    }
-
-    private void checkPolicy()
-    {
-        //
-        // 6.1.1 Inputs
-        //
-
-        // c) Initial Policy Set
-
-        Set userInitialPolicySet = pkixParams.getInitialPolicies();
-
-        // e) f) g) are part of pkixParams
-
-        //
-        // 6.1.2 Initialization
-        //
-
-        // a) valid policy tree
-
-        List[] policyNodes = new ArrayList[n + 1];
-        for (int j = 0; j < policyNodes.length; j++)
-        {
-            policyNodes[j] = new ArrayList();
-        }
-
-        Set policySet = new HashSet();
-
-        policySet.add(ANY_POLICY);
-
-        PKIXPolicyNode validPolicyTree = new PKIXPolicyNode(new ArrayList(), 0,
-                policySet, null, new HashSet(), ANY_POLICY, false);
-
-        policyNodes[0].add(validPolicyTree);
-
-        // d) explicit policy
-
-        int explicitPolicy;
-        if (pkixParams.isExplicitPolicyRequired())
-        {
-            explicitPolicy = 0;
-        }
-        else
-        {
-            explicitPolicy = n + 1;
-        }
-
-        // e) inhibit any policy
-
-        int inhibitAnyPolicy;
-        if (pkixParams.isAnyPolicyInhibited())
-        {
-            inhibitAnyPolicy = 0;
-        }
-        else
-        {
-            inhibitAnyPolicy = n + 1;
-        }
-
-        // f) policy mapping
-
-        int policyMapping;
-        if (pkixParams.isPolicyMappingInhibited())
-        {
-            policyMapping = 0;
-        }
-        else
-        {
-            policyMapping = n + 1;
-        }
-
-        Set acceptablePolicies = null;
-
-        //
-        // 6.1.3 Basic Certificate processing
-        //
-
-        X509Certificate cert = null;
-        int index;
-        int i;
-
-        try 
-        {
-            for (index = certs.size() - 1; index >= 0; index--)
-            {
-                // i as defined in the algorithm description
-                i = n - index;
-    
-                // set certificate to be checked in this round
-                cert = (X509Certificate) certs.get(index);
-    
-                // d) process policy information
-    
-                ASN1Sequence certPolicies;
-                try 
-                {
-                    certPolicies = (ASN1Sequence) getExtensionValue(
-                        cert, CERTIFICATE_POLICIES);
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyExtError");
-                    throw new CertPathReviewerException(msg,ae,certPath,index);
-                }
-                if (certPolicies != null && validPolicyTree != null)
-                {
-
-                    // d) 1)
-
-                    Enumeration e = certPolicies.getObjects();
-                    Set pols = new HashSet();
-
-                    while (e.hasMoreElements())
-                    {
-                        PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
-                        DERObjectIdentifier pOid = pInfo.getPolicyIdentifier();
-
-                        pols.add(pOid.getId());
-
-                        if (!ANY_POLICY.equals(pOid.getId()))
-                        {
-                            Set pq;
-                            try
-                            {
-                                pq = getQualifierSet(pInfo.getPolicyQualifiers());
-                            }
-                            catch (CertPathValidatorException cpve)
-                            {
-                                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyQualifierError");
-                                throw new CertPathReviewerException(msg,cpve,certPath,index);
-                            }
-
-                            boolean match = processCertD1i(i, policyNodes, pOid, pq);
-
-                            if (!match)
-                            {
-                                processCertD1ii(i, policyNodes, pOid, pq);
-                            }
-                        }
-                    }
-
-                    if (acceptablePolicies == null || acceptablePolicies.contains(ANY_POLICY))
-                    {
-                        acceptablePolicies = pols;
-                    }
-                    else
-                    {
-                        Iterator it = acceptablePolicies.iterator();
-                        Set t1 = new HashSet();
-
-                        while (it.hasNext())
-                        {
-                            Object o = it.next();
-
-                            if (pols.contains(o))
-                            {
-                                t1.add(o);
-                            }
-                        }
-
-                        acceptablePolicies = t1;
-                    }
-
-                    // d) 2)
-
-                    if ((inhibitAnyPolicy > 0) || ((i < n) && isSelfIssued(cert)))
-                    {
-                        e = certPolicies.getObjects();
-
-                        while (e.hasMoreElements())
-                        {
-                            PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
-
-                            if (ANY_POLICY.equals(pInfo.getPolicyIdentifier().getId()))
-                            {
-                                Set _apq;
-                                try
-                                {
-                                    _apq = getQualifierSet(pInfo.getPolicyQualifiers());
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyQualifierError");
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                List _nodes = policyNodes[i - 1];
-
-                                for (int k = 0; k < _nodes.size(); k++)
-                                {
-                                    PKIXPolicyNode _node = (PKIXPolicyNode) _nodes.get(k);
-
-                                    Iterator _policySetIter = _node.getExpectedPolicies().iterator();
-                                    while (_policySetIter.hasNext())
-                                    {
-                                        Object _tmp = _policySetIter.next();
-
-                                        String _policy;
-                                        if (_tmp instanceof String)
-                                        {
-                                            _policy = (String) _tmp;
-                                        }
-                                        else if (_tmp instanceof DERObjectIdentifier)
-                                        {
-                                            _policy = ((DERObjectIdentifier) _tmp).getId();
-                                        }
-                                        else
-                                        {
-                                            continue;
-                                        }
-
-                                        boolean _found = false;
-                                        Iterator _childrenIter = _node
-                                                .getChildren();
-
-                                        while (_childrenIter.hasNext())
-                                        {
-                                            PKIXPolicyNode _child = (PKIXPolicyNode) _childrenIter.next();
-
-                                            if (_policy.equals(_child.getValidPolicy()))
-                                            {
-                                                _found = true;
-                                            }
-                                        }
-
-                                        if (!_found)
-                                        {
-                                            Set _newChildExpectedPolicies = new HashSet();
-                                            _newChildExpectedPolicies.add(_policy);
-
-                                            PKIXPolicyNode _newChild = new PKIXPolicyNode(
-                                                    new ArrayList(), i,
-                                                    _newChildExpectedPolicies,
-                                                    _node, _apq, _policy, false);
-                                            _node.addChild(_newChild);
-                                            policyNodes[i].add(_newChild);
-                                        }
-                                    }
-                                }
-                                break;
-                            }
-                        }
-                    }
-
-                    //
-                    // (d) (3)
-                    //
-                    for (int j = (i - 1); j >= 0; j--)
-                    {
-                        List nodes = policyNodes[j];
-
-                        for (int k = 0; k < nodes.size(); k++)
-                        {
-                            PKIXPolicyNode node = (PKIXPolicyNode) nodes.get(k);
-                            if (!node.hasChildren())
-                            {
-                                validPolicyTree = removePolicyNode(
-                                        validPolicyTree, policyNodes, node);
-                                if (validPolicyTree == null)
-                                {
-                                    break;
-                                }
-                            }
-                        }
-                    }
-
-                    //
-                    // d (4)
-                    //
-                    Set criticalExtensionOids = cert.getCriticalExtensionOIDs();
-
-                    if (criticalExtensionOids != null)
-                    {
-                        boolean critical = criticalExtensionOids.contains(CERTIFICATE_POLICIES);
-
-                        List nodes = policyNodes[i];
-                        for (int j = 0; j < nodes.size(); j++)
-                        {
-                            PKIXPolicyNode node = (PKIXPolicyNode) nodes.get(j);
-                            node.setCritical(critical);
-                        }
-                    }
-
-                }
-                
-                // e)
-                
-                if (certPolicies == null) 
-                {
-                    validPolicyTree = null;
-                }
-                
-                // f)
-                
-                if (explicitPolicy <= 0 && validPolicyTree == null)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noValidPolicyTree");
-                    throw new CertPathReviewerException(msg);
-                }
-    
-                //
-                // 6.1.4 preparation for next Certificate
-                //
-    
-                if (i != n)
-                {
-                    
-                    // a)
-                    
-                    DERObject pm;
-                    try
-                    {
-                        pm = getExtensionValue(cert, POLICY_MAPPINGS);
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyMapExtError");
-                        throw new CertPathReviewerException(msg,ae,certPath,index);
-                    }
-                    
-                    if (pm != null) 
-                    {
-                        ASN1Sequence mappings = (ASN1Sequence) pm;
-                        for (int j = 0; j < mappings.size(); j++) 
-                        {
-                            ASN1Sequence mapping = (ASN1Sequence) mappings.getObjectAt(j);
-                            DERObjectIdentifier ip_id = (DERObjectIdentifier) mapping.getObjectAt(0);
-                            DERObjectIdentifier sp_id = (DERObjectIdentifier) mapping.getObjectAt(1);
-                            if (ANY_POLICY.equals(ip_id.getId())) 
-                            {
-                                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.invalidPolicyMapping");
-                                throw new CertPathReviewerException(msg,certPath,index);
-                            }
-                            if (ANY_POLICY.equals(sp_id.getId()))
-                            {
-                                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.invalidPolicyMapping");
-                                throw new CertPathReviewerException(msg,certPath,index);
-                            }
-                        }
-                    }
-                    
-                    // b)
-                    
-                    if (pm != null)
-                    {
-                        ASN1Sequence mappings = (ASN1Sequence)pm;
-                        Map m_idp = new HashMap();
-                        Set s_idp = new HashSet();
-                        
-                        for (int j = 0; j < mappings.size(); j++)
-                        {
-                            ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j);
-                            String id_p = ((DERObjectIdentifier)mapping.getObjectAt(0)).getId();
-                            String sd_p = ((DERObjectIdentifier)mapping.getObjectAt(1)).getId();
-                            Set tmp;
-                            
-                            if (!m_idp.containsKey(id_p))
-                            {
-                                tmp = new HashSet();
-                                tmp.add(sd_p);
-                                m_idp.put(id_p, tmp);
-                                s_idp.add(id_p);
-                            }
-                            else
-                            {
-                                tmp = (Set)m_idp.get(id_p);
-                                tmp.add(sd_p);
-                            }
-                        }
-    
-                        Iterator it_idp = s_idp.iterator();
-                        while (it_idp.hasNext())
-                        {
-                            String id_p = (String)it_idp.next();
-                            
-                            //
-                            // (1)
-                            //
-                            if (policyMapping > 0)
-                            {
-                                try
-                                {
-                                    prepareNextCertB1(i,policyNodes,id_p,m_idp,cert);
-                                }
-                                catch (AnnotatedException ae)
-                                {
-                                    // error processing certificate policies extension
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyExtError");
-                                    throw new CertPathReviewerException(msg,ae,certPath,index);
-                                }
-                                catch (CertPathValidatorException cpve)
-                                {
-                                    // error building qualifier set
-                                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyQualifierError");
-                                    throw new CertPathReviewerException(msg,cpve,certPath,index);
-                                }
-                                
-                                //
-                                // (2)
-                                // 
-                            }
-                            else if (policyMapping <= 0)
-                            {
-                                validPolicyTree = prepareNextCertB2(i,policyNodes,id_p,validPolicyTree);
-                            }
-                            
-                        }
-                    }
-                    
-                    //
-                    // h)
-                    //
-                    
-                    if (!isSelfIssued(cert)) 
-                    {
-                        
-                        // (1)
-                        if (explicitPolicy != 0)
-                        {
-                            explicitPolicy--;
-                        }
-                        
-                        // (2)
-                        if (policyMapping != 0)
-                        {
-                            policyMapping--;
-                        }
-                        
-                        // (3)
-                        if (inhibitAnyPolicy != 0)
-                        {
-                            inhibitAnyPolicy--;
-                        }
-                        
-                    }
-    
-                    //
-                    // i)
-                    //
-                    
-                    try
-                    {
-                        ASN1Sequence pc = (ASN1Sequence) getExtensionValue(cert,POLICY_CONSTRAINTS);
-                        if (pc != null)
-                        {
-                            Enumeration policyConstraints = pc.getObjects();
-                            
-                            while (policyConstraints.hasMoreElements())
-                            {
-                                ASN1TaggedObject constraint = (ASN1TaggedObject) policyConstraints.nextElement();
-                                int tmpInt; 
-                                
-                                switch (constraint.getTagNo())
-                                {
-                                case 0:
-                                    tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                                    if (tmpInt < explicitPolicy)
-                                    {
-                                        explicitPolicy = tmpInt;
-                                    }
-                                    break;
-                                case 1:
-                                    tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                                    if (tmpInt < policyMapping)
-                                    {
-                                        policyMapping = tmpInt;
-                                    }
-                                break;
-                                }
-                            }
-                        }
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyConstExtError");
-                        throw new CertPathReviewerException(msg,certPath,index);
-                    }
-    
-                    //
-                    // j)
-                    //
-                    
-                    try 
-                    {
-                        DERInteger iap = (DERInteger)getExtensionValue(cert, INHIBIT_ANY_POLICY);
-                        
-                        if (iap != null)
-                        {
-                            int _inhibitAnyPolicy = iap.getValue().intValue();
-                        
-                            if (_inhibitAnyPolicy < inhibitAnyPolicy)
-                            {
-                                inhibitAnyPolicy = _inhibitAnyPolicy;
-                            }
-                        }
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyInhibitExtError");
-                        throw new CertPathReviewerException(msg,certPath,index);
-                    }
-                }
-    
-            }
-    
-            //
-            // 6.1.5 Wrap up
-            //
-    
-            //
-            // a)
-            //
-            
-            if (!isSelfIssued(cert) && explicitPolicy > 0) 
-            {
-                explicitPolicy--;
-            }
-    
-            //
-            // b)
-            //
-            
-            try
-            {
-                ASN1Sequence pc = (ASN1Sequence) getExtensionValue(cert, POLICY_CONSTRAINTS);
-                if (pc != null)
-                {
-                    Enumeration policyConstraints = pc.getObjects();
-        
-                    while (policyConstraints.hasMoreElements())
-                    {
-                        ASN1TaggedObject    constraint = (ASN1TaggedObject)policyConstraints.nextElement();
-                        switch (constraint.getTagNo())
-                        {
-                        case 0:
-                            int tmpInt = DERInteger.getInstance(constraint).getValue().intValue();
-                            if (tmpInt == 0)
-                            {
-                                explicitPolicy = 0;
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-            catch (AnnotatedException e)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.policyConstExtError");
-                throw new CertPathReviewerException(msg,certPath,index);
-            }
-            
-            
-            //
-            // (g)
-            //
-            PKIXPolicyNode intersection;
-            
-    
-            //
-            // (g) (i)
-            //
-            if (validPolicyTree == null)
-            { 
-                if (pkixParams.isExplicitPolicyRequired())
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.explicitPolicy");
-                    throw new CertPathReviewerException(msg,certPath,index);
-                }
-                intersection = null;
-            }
-            else if (isAnyPolicy(userInitialPolicySet)) // (g) (ii)
-            {
-                if (pkixParams.isExplicitPolicyRequired())
-                {
-                    if (acceptablePolicies.isEmpty())
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.explicitPolicy");
-                        throw new CertPathReviewerException(msg,certPath,index);
-                    }
-                    else
-                    {
-                        Set _validPolicyNodeSet = new HashSet();
-                        
-                        for (int j = 0; j < policyNodes.length; j++)
-                        {
-                            List      _nodeDepth = policyNodes[j];
-                            
-                            for (int k = 0; k < _nodeDepth.size(); k++)
-                            {
-                                PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
-                                
-                                if (ANY_POLICY.equals(_node.getValidPolicy()))
-                                {
-                                    Iterator _iter = _node.getChildren();
-                                    while (_iter.hasNext())
-                                    {
-                                        _validPolicyNodeSet.add(_iter.next());
-                                    }
-                                }
-                            }
-                        }
-                        
-                        Iterator _vpnsIter = _validPolicyNodeSet.iterator();
-                        while (_vpnsIter.hasNext())
-                        {
-                            PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
-                            String _validPolicy = _node.getValidPolicy();
-                            
-                            if (!acceptablePolicies.contains(_validPolicy))
-                            {
-                                //validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, _node);
-                            }
-                        }
-                        if (validPolicyTree != null)
-                        {
-                            for (int j = (n - 1); j >= 0; j--)
-                            {
-                                List      nodes = policyNodes[j];
-                                
-                                for (int k = 0; k < nodes.size(); k++)
-                                {
-                                    PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
-                                    if (!node.hasChildren())
-                                    {
-                                        validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, node);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-    
-                intersection = validPolicyTree;
-            }
-            else
-            {
-                //
-                // (g) (iii)
-                //
-                // This implementation is not exactly same as the one described in RFC3280.
-                // However, as far as the validation result is concerned, both produce 
-                // adequate result. The only difference is whether AnyPolicy is remain 
-                // in the policy tree or not. 
-                //
-                // (g) (iii) 1
-                //
-                Set _validPolicyNodeSet = new HashSet();
-                
-                for (int j = 0; j < policyNodes.length; j++)
-                {
-                    List      _nodeDepth = policyNodes[j];
-                    
-                    for (int k = 0; k < _nodeDepth.size(); k++)
-                    {
-                        PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
-                        
-                        if (ANY_POLICY.equals(_node.getValidPolicy()))
-                        {
-                            Iterator _iter = _node.getChildren();
-                            while (_iter.hasNext())
-                            {
-                                PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next();
-                                if (!ANY_POLICY.equals(_c_node.getValidPolicy()))
-                                {
-                                    _validPolicyNodeSet.add(_c_node);
-                                }
-                            }
-                        }
-                    }
-                }
-                
-                //
-                // (g) (iii) 2
-                //
-                Iterator _vpnsIter = _validPolicyNodeSet.iterator();
-                while (_vpnsIter.hasNext())
-                {
-                    PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
-                    String _validPolicy = _node.getValidPolicy();
-    
-                    if (!userInitialPolicySet.contains(_validPolicy))
-                    {
-                        validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, _node);
-                    }
-                }
-                
-                //
-                // (g) (iii) 4
-                //
-                if (validPolicyTree != null)
-                {
-                    for (int j = (n - 1); j >= 0; j--)
-                    {
-                        List      nodes = policyNodes[j];
-                        
-                        for (int k = 0; k < nodes.size(); k++)
-                        {
-                            PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
-                            if (!node.hasChildren())
-                            {
-                                validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, node);
-                            }
-                        }
-                    }
-                }
-                
-                intersection = validPolicyTree;
-            }
-     
-            if ((explicitPolicy <= 0) && (intersection == null))
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.invalidPolicy");
-                throw new CertPathReviewerException(msg);
-            }
-            
-            validPolicyTree = intersection;
-        }
-        catch (CertPathReviewerException cpre)
-        {
-            addError(cpre.getErrorMessage(),cpre.getIndex());
-            validPolicyTree = null;
-        }
-    }
-
-    private void checkCriticalExtensions()
-    {
-        //      
-        // initialise CertPathChecker's
-        //
-        List  pathCheckers = pkixParams.getCertPathCheckers();
-        Iterator certIter = pathCheckers.iterator();
-        
-        try
-        {
-            try
-            {
-                while (certIter.hasNext())
-                {
-                    ((PKIXCertPathChecker)certIter.next()).init(false);
-                }
-            }
-            catch (CertPathValidatorException cpve)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certPathCheckerError",
-                        new Object[] {cpve.getMessage(),cpve});
-                throw new CertPathReviewerException(msg,cpve);
-            }
-            
-            //
-            // process critical extesions for each certificate
-            //
-            
-            X509Certificate cert = null;
-            
-            int index;
-            
-            for (index = certs.size()-1; index >= 0; index--)
-            {
-                cert = (X509Certificate) certs.get(index);
-                
-                Set criticalExtensions = new HashSet(cert.getCriticalExtensionOIDs());
-                // remove already processed extensions
-                criticalExtensions.remove(KEY_USAGE);
-                criticalExtensions.remove(CERTIFICATE_POLICIES);
-                criticalExtensions.remove(POLICY_MAPPINGS);
-                criticalExtensions.remove(INHIBIT_ANY_POLICY);
-                criticalExtensions.remove(ISSUING_DISTRIBUTION_POINT);
-                criticalExtensions.remove(DELTA_CRL_INDICATOR);
-                criticalExtensions.remove(POLICY_CONSTRAINTS);
-                criticalExtensions.remove(BASIC_CONSTRAINTS);
-                criticalExtensions.remove(SUBJECT_ALTERNATIVE_NAME);
-                criticalExtensions.remove(NAME_CONSTRAINTS);
-                
-                // process qcStatements extension
-                if (criticalExtensions.contains(QC_STATEMENT))
-                {
-                    if (processQcStatements(cert,index)) 
-                    {
-                        criticalExtensions.remove(QC_STATEMENT);
-                    }
-                }
-                
-                Iterator tmpIter = pathCheckers.iterator();
-                while (tmpIter.hasNext())
-                {
-                    try
-                    {
-                        ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
-                    }
-                    catch (CertPathValidatorException e)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.criticalExtensionError",
-                                new Object[] {e.getMessage(),e});
-                        throw new CertPathReviewerException(msg,e.getCause(),certPath,index);
-                    }
-                }
-                if (!criticalExtensions.isEmpty())
-                {
-                    ErrorBundle msg;
-                    if (criticalExtensions.size() == 1)
-                    {
-                        msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.unknownCriticalExt",
-                                new Object[] {criticalExtensions.iterator().next()});
-                    }
-                    else
-                    {
-                        msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.unknownCriticalExts",
-                                new Object[] {new UntrustedInput(criticalExtensions)});
-                    }
-                    throw new CertPathReviewerException(msg,certPath,index);
-                }
-            }
-        }
-        catch (CertPathReviewerException cpre)
-        {
-            addError(cpre.getErrorMessage(),cpre.getIndex());
-        }
-    }
-    
-    private boolean processQcStatements(
-            X509Certificate cert,
-            int index)
-    {   
-        try
-        {
-            boolean unknownStatement = false;
-            
-            ASN1Sequence qcSt = (ASN1Sequence) getExtensionValue(cert,QC_STATEMENT);
-            for (int j = 0; j < qcSt.size(); j++)
-            {
-                QCStatement stmt = QCStatement.getInstance(qcSt.getObjectAt(j));
-                if (QCStatement.id_etsi_qcs_QcCompliance.equals(stmt.getStatementId()))
-                {
-                    // process statement - just write a notification that the certificate contains this statement
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcEuCompliance");
-                    addNotification(msg,index);
-                }
-                else if (QCStatement.id_qcs_pkixQCSyntax_v1.equals(stmt.getStatementId()))
-                {
-                    // process statement - just recognize the statement
-                }
-                else if (QCStatement.id_etsi_qcs_QcSSCD.equals(stmt.getStatementId()))
-                {
-                    // process statement - just write a notification that the certificate contains this statement
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcSSCD");
-                    addNotification(msg,index);
-                }
-                else if (QCStatement.id_etsi_qcs_LimiteValue.equals(stmt.getStatementId()))
-                {
-                    // process statement - write a notification containing the limit value
-                    MonetaryValue limit = MonetaryValue.getInstance(stmt.getStatementInfo());
-                    Iso4217CurrencyCode currency = limit.getCurrency();
-                    double value = limit.getAmount().doubleValue() * Math.pow(10,limit.getExponent().doubleValue());
-                    ErrorBundle msg;
-                    if (limit.getCurrency().isAlphabetic())
-                    {
-                        msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcLimitValueAlpha",
-                                new Object[] {limit.getCurrency().getAlphabetic(),
-                                              new Double(value),
-                                              limit});
-                    }
-                    else
-                    {
-                        msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcLimitValueNum",
-                                new Object[] {Integer.valueOf(limit.getCurrency().getNumeric()),
-                                              new Double(value),
-                                              limit});
-                    }
-                    addNotification(msg,index);
-                }
-                else
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcUnknownStatement",
-                            new Object[] {stmt.getStatementId(),new UntrustedInput(stmt)});
-                    addNotification(msg,index);
-                    unknownStatement = true;
-                }
-            }
-            
-            return !unknownStatement;
-        }
-        catch (AnnotatedException ae)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.QcStatementExtError");
-            addError(msg,index);
-        }
-        
-        return false;
-    }
-    
-    private String IPtoString(byte[] ip)
-    {
-        String result;
-        try
-        {
-            result = InetAddress.getByAddress(ip).getHostAddress();
-        }
-        catch (Exception e)
-        {
-            StringBuffer b = new StringBuffer();
-            
-            for (int i = 0; i != ip.length; i++)
-            {
-                b.append(Integer.toHexString(ip[i] & 0xff));
-                b.append(' ');
-            }
-            
-            result = b.toString();
-        }
-        
-        return result;
-    }
-    
-    private void checkCRLs(
-            PKIXParameters paramsPKIX,
-            X509Certificate cert,
-            Date validDate,
-            X509Certificate sign,
-            PublicKey workingPublicKey,
-            Vector crlDistPointUrls,
-            int index) 
-        throws CertPathReviewerException
-    {
-        X509CRLSelector crlselect;
-        crlselect = new X509CRLSelector();
-        
-        try
-        {
-            crlselect.addIssuerName(getEncodedIssuerPrincipal(cert).getEncoded());
-        }
-        catch (IOException e)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlIssuerException");
-            throw new CertPathReviewerException(msg,e);
-        }
-    
-        crlselect.setCertificateChecking(cert);
-    
-        Iterator crl_iter;
-        try 
-        {
-            Collection crl_coll = findCRLs(crlselect, paramsPKIX.getCertStores());
-            crl_iter = crl_coll.iterator();
-            
-            if (crl_coll.isEmpty())
-            {
-                // notifcation - no local crls found
-                crl_coll = findCRLs(new X509CRLSelector(),paramsPKIX.getCertStores());
-                Iterator it = crl_coll.iterator();
-                List nonMatchingCrlNames = new ArrayList();
-                while (it.hasNext())
-                {
-                    nonMatchingCrlNames.add(((X509CRL) it.next()).getIssuerX500Principal());
-                }
-                int numbOfCrls = nonMatchingCrlNames.size();
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                        "CertPathReviewer.noCrlInCertstore",
-                        new Object[] {new UntrustedInput(crlselect.getIssuers()),
-                                      new UntrustedInput(nonMatchingCrlNames),
-                                      Integer.valueOf(numbOfCrls)});
-                addNotification(msg,index);
-            }
-
-        }
-        catch (AnnotatedException ae)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlExtractionError",
-                    new Object[] {ae.getCause().getMessage(),ae.getCause()});
-            addError(msg,index);
-            crl_iter = new ArrayList().iterator();
-        }
-        boolean validCrlFound = false;
-        X509CRL crl = null;
-        while (crl_iter.hasNext())
-        {
-            crl = (X509CRL)crl_iter.next();
-            
-            if (crl.getNextUpdate() == null
-                || new Date().before(crl.getNextUpdate()))
-            {
-                validCrlFound = true;
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                        "CertPathReviewer.localValidCRL",
-                        new Object[] {crl.getThisUpdate(),crl.getNextUpdate()});
-                addNotification(msg,index);
-                break;
-            }
-            else
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                        "CertPathReviewer.localInvalidCRL",
-                        new Object[] {crl.getThisUpdate(),crl.getNextUpdate()});
-                addNotification(msg,index);
-            }
-        }
-        
-        // if no valid crl was found in the CertStores try to get one from a
-        // crl distribution point
-        if (!validCrlFound)
-        {
-            X509CRL onlineCRL = null;
-            Iterator urlIt = crlDistPointUrls.iterator();
-            while (urlIt.hasNext())
-            {
-                try
-                {
-                    String location = (String) urlIt.next();
-                    onlineCRL = getCRL(location);
-                    if (onlineCRL != null)
-                    {
-                        if (onlineCRL.getNextUpdate() == null
-                            || new Date().before(onlineCRL.getNextUpdate()))
-                        {
-                            validCrlFound = true;
-                            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                                    "CertPathReviewer.onlineValidCRL",
-                                    new Object[] {onlineCRL.getThisUpdate(),
-                                                  onlineCRL.getNextUpdate(),
-                                                  new UntrustedInput(location)});
-                            addNotification(msg,index);
-                            crl = onlineCRL;
-                            break;
-                        }
-                        else
-                        {
-                            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                                    "CertPathReviewer.onlineInvalidCRL",
-                                    new Object[] {onlineCRL.getThisUpdate(),
-                                                  onlineCRL.getNextUpdate(),
-                                                  new UntrustedInput(location)});
-                            addNotification(msg,index);
-                        }
-                    }
-                }
-                catch (CertPathReviewerException cpre)
-                {
-                    addNotification(cpre.getErrorMessage(),index);
-                }
-            }
-        }
-        
-        // check the crl
-        X509CRLEntry crl_entry;
-        if (crl != null)
-        {
-            if (sign != null)
-            {
-                boolean[] keyusage = sign.getKeyUsage();
-
-                if (keyusage != null
-                    && (keyusage.length < 7 || !keyusage[CRL_SIGN]))
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noCrlSigningPermited");
-                    throw new CertPathReviewerException(msg);
-                }
-            }
-
-            if (workingPublicKey != null)
-            {
-                try
-                {
-                    crl.verify(workingPublicKey, "BC");
-                }
-                catch (Exception e)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlVerifyFailed");
-                    throw new CertPathReviewerException(msg,e);
-                }
-            }
-            else // issuer public key not known
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlNoIssuerPublicKey");
-                throw new CertPathReviewerException(msg);
-            }
-
-            crl_entry = crl.getRevokedCertificate(cert.getSerialNumber());
-            if (crl_entry != null)
-            {
-                String reason = null;
-                
-                if (crl_entry.hasExtensions())
-                {
-                    DEREnumerated reasonCode;
-                    try
-                    {
-                        reasonCode = DEREnumerated.getInstance(getExtensionValue(crl_entry, X509Extensions.ReasonCode.getId()));
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlReasonExtError");
-                        throw new CertPathReviewerException(msg,ae);
-                    }
-                    if (reasonCode != null)
-                    {
-                        reason = crlReasons[reasonCode.getValue().intValue()];
-                    }
-                }
-                
-                // FIXME reason not i18n
-                
-                if (!validDate.before(crl_entry.getRevocationDate()))
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.certRevoked",
-                            new Object[] {crl_entry.getRevocationDate(),reason});
-                    throw new CertPathReviewerException(msg);
-                }
-                else // cert was revoked after validation date
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.revokedAfterValidation",
-                            new Object[] {crl_entry.getRevocationDate(),reason});
-                    addNotification(msg,index);
-                }
-            }
-            else // cert is not revoked
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.notRevoked");
-                addNotification(msg,index);
-            }
-            
-            //
-            // warn if a new crl is available
-            //
-            if (crl.getNextUpdate() != null && crl.getNextUpdate().before(new Date()))
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlUpdateAvailable",
-                        new Object[] {crl.getNextUpdate()});
-                addNotification(msg,index);
-            }
-            
-            //
-            // check the DeltaCRL indicator, base point and the issuing distribution point
-            //
-            DERObject idp;
-            try
-            {
-                idp = getExtensionValue(crl, ISSUING_DISTRIBUTION_POINT);
-            }
-            catch (AnnotatedException ae)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.distrPtExtError");
-                throw new CertPathReviewerException(msg);
-            }
-            DERObject dci;
-            try
-            {
-                dci = getExtensionValue(crl, DELTA_CRL_INDICATOR);
-            }
-            catch (AnnotatedException ae)
-            {
-                ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.deltaCrlExtError");
-                throw new CertPathReviewerException(msg);
-            }
-
-            if (dci != null)
-            {
-                X509CRLSelector baseSelect = new X509CRLSelector();
-
-                try
-                {
-                    baseSelect.addIssuerName(getIssuerPrincipal(crl).getEncoded());
-                }
-                catch (IOException e)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlIssuerException");
-                    throw new CertPathReviewerException(msg,e);
-                }
-
-                baseSelect.setMinCRLNumber(((DERInteger)dci).getPositiveValue());
-                try
-                {
-                    baseSelect.setMaxCRLNumber(((DERInteger)getExtensionValue(crl, CRL_NUMBER)).getPositiveValue().subtract(BigInteger.valueOf(1)));
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlNbrExtError");
-                    throw new CertPathReviewerException(msg,ae);
-                }
-                
-                boolean  foundBase = false;
-                Iterator it;
-                try 
-                {
-                    it  = findCRLs(baseSelect, paramsPKIX.getCertStores()).iterator();
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlExtractionError");
-                    throw new CertPathReviewerException(msg,ae);
-                }
-                while (it.hasNext())
-                {
-                    X509CRL base = (X509CRL)it.next();
-
-                    DERObject baseIdp;
-                    try
-                    {
-                        baseIdp = getExtensionValue(base, ISSUING_DISTRIBUTION_POINT);
-                    }
-                    catch (AnnotatedException ae)
-                    {
-                        ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.distrPtExtError");
-                        throw new CertPathReviewerException(msg,ae);
-                    }
-                    
-                    if (idp == null)
-                    {
-                        if (baseIdp == null)
-                        {
-                            foundBase = true;
-                            break;
-                        }
-                    }
-                    else
-                    {
-                        if (idp.equals(baseIdp))
-                        {
-                            foundBase = true;
-                            break;
-                        }
-                    }
-                }
-                
-                if (!foundBase)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noBaseCRL");
-                    throw new CertPathReviewerException(msg);
-                }
-            }
-
-            if (idp != null)
-            {
-                IssuingDistributionPoint    p = IssuingDistributionPoint.getInstance(idp);
-                BasicConstraints bc = null;
-                try
-                {
-                    bc = BasicConstraints.getInstance(getExtensionValue(cert, BASIC_CONSTRAINTS));
-                }
-                catch (AnnotatedException ae)
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlBCExtError");
-                    throw new CertPathReviewerException(msg,ae);
-                }
-                
-                if (p.onlyContainsUserCerts() && (bc != null && bc.isCA()))
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlOnlyUserCert");
-                    throw new CertPathReviewerException(msg);
-                }
-                
-                if (p.onlyContainsCACerts() && (bc == null || !bc.isCA()))
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlOnlyCaCert");
-                    throw new CertPathReviewerException(msg);
-                }
-                
-                if (p.onlyContainsAttributeCerts())
-                {
-                    ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlOnlyAttrCert");
-                    throw new CertPathReviewerException(msg);
-                }
-            }
-        }
-        
-        if (!validCrlFound)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.noValidCrlFound");
-            throw new CertPathReviewerException(msg);
-        }
-    
-    }
-    
-    private Vector getCRLDistUrls(CRLDistPoint crlDistPoints, AuthorityInformationAccess authInfoAcc)
-    {
-        Vector urls = new Vector();
-        
-        if (crlDistPoints != null)
-        {
-            DistributionPoint[] distPoints = crlDistPoints.getDistributionPoints();
-            for (int i = 0; i < distPoints.length; i++)
-            {
-                DistributionPointName dp_name = distPoints[i].getDistributionPoint();
-                if (dp_name.getType() == DistributionPointName.FULL_NAME)
-                {
-                    GeneralName[] generalNames = GeneralNames.getInstance(dp_name.getName()).getNames();
-                    for (int j = 0; j < generalNames.length; j++)
-                    {
-                        if (generalNames[j].getTagNo() == GeneralName.uniformResourceIdentifier)
-                        {
-                            String url = ((DERIA5String) generalNames[j].getName()).getString();
-                            urls.add(url);
-                        }
-                    }
-                }
-            }
-        }
-        
-        if (authInfoAcc != null)
-        {
-            AccessDescription[] ads = authInfoAcc.getAccessDescriptions();
-            for (int i = 0; i < ads.length; i++)
-            {
-                if (ads[i].getAccessMethod().equals(AccessDescription.id_ad_caIssuers))
-                {
-                    GeneralName name = ads[i].getAccessLocation();
-                    if (name.getTagNo() ==  GeneralName.uniformResourceIdentifier)
-                    {
-                        String url = ((DERIA5String) name.getName()).getString();
-                        urls.add(url);
-                    }
-                }
-            }
-        }
-        
-        return urls;
-    }
-    
-    private Vector getOCSPUrls(AuthorityInformationAccess authInfoAccess)
-    {
-        Vector urls = new Vector();
-        
-        if (authInfoAccess != null)
-        {
-            AccessDescription[] ads = authInfoAccess.getAccessDescriptions();
-            for (int i = 0; i < ads.length; i++)
-            {
-                if (ads[i].getAccessMethod().equals(AccessDescription.id_ad_ocsp))
-                {
-                    GeneralName name = ads[i].getAccessLocation();
-                    if (name.getTagNo() == GeneralName.uniformResourceIdentifier)
-                    {
-                        String url = ((DERIA5String) name.getName()).getString();
-                        urls.add(url);
-                    }
-                }
-            }
-        }
-        
-        return urls;
-    }
-    
-    private X509CRL getCRL(String location) throws CertPathReviewerException
-    {
-        X509CRL result = null;
-        try
-        {
-            URL url = new URL(location);
-            
-            if (url.getProtocol().equals("http") || url.getProtocol().equals("https"))
-            {
-                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-                conn.setUseCaches(false);
-                conn.setConnectTimeout(2000);
-                conn.setDoInput(true);
-                conn.connect();
-                if (conn.getResponseCode() == HttpURLConnection.HTTP_OK)
-                {
-                    CertificateFactory cf = CertificateFactory.getInstance("X.509","BC");
-                    result = (X509CRL) cf.generateCRL(conn.getInputStream());
-                }
-                else
-                {
-                    throw new Exception(conn.getResponseMessage());
-                }
-            }
-        }
-        catch (Exception e)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
-                    "CertPathReviewer.loadCrlDistPointError",
-                    new Object[] {new UntrustedInput(location),
-                                  e.getMessage(),e});
-            throw new CertPathReviewerException(msg);
-        }
-        return result;
-    }
-    
-    private Collection getTrustAnchors(X509Certificate cert, Set trustanchors) throws CertPathReviewerException
-    {
-        Collection trustColl = new ArrayList();
-        Iterator it = trustanchors.iterator();
-        
-        X509CertSelector certSelectX509 = new X509CertSelector();
-
-        try
-        {
-            certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded());
-        }
-        catch (IOException ex)
-        {
-            ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.trustAnchorIssuerError");
-            throw new CertPathReviewerException(msg);
-        }
-
-        while (it.hasNext())
-        {
-            TrustAnchor trust = (TrustAnchor) it.next();
-            if (trust.getTrustedCert() != null)
-            {
-                if (certSelectX509.match(trust.getTrustedCert()))
-                {
-                    trustColl.add(trust);
-                }
-            }
-            else if (trust.getCAName() != null && trust.getCAPublicKey() != null)
-            {
-                X500Principal certIssuer = getEncodedIssuerPrincipal(cert);
-                X500Principal caName = new X500Principal(trust.getCAName());
-                if (certIssuer.equals(caName))
-                {
-                    trustColl.add(trust);
-                }
-            }
-        }
-        return trustColl;
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/X509AttributeCertificate.java b/src/main/java/org/bouncycastle/x509/X509AttributeCertificate.java
index f3b0668..48a825f 100644
--- a/src/main/java/org/bouncycastle/x509/X509AttributeCertificate.java
+++ b/src/main/java/org/bouncycastle/x509/X509AttributeCertificate.java
@@ -12,7 +12,6 @@
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Extension;
 import java.util.Date;
-import java.util.Set;
 
 /**
  * Interface for an X.509 Attribute Certificate.
@@ -79,10 +78,6 @@
     
     public boolean[] getIssuerUniqueID();
     
-    public Set getNonCriticalExtensionOIDs();
-
-    public Set getCriticalExtensionOIDs();
-    
     public void checkValidity()
         throws CertificateExpiredException, CertificateNotYetValidException;
     
diff --git a/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java b/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java
new file mode 100644
index 0000000..cc50b8f
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/X509CRLStoreSelector.java
@@ -0,0 +1,330 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.x509.extension.X509ExtensionUtil;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CRL;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLSelector;
+
+/**
+ * This class is a Selector implementation for X.509 certificate revocation
+ * lists.
+ * 
+ * @see org.bouncycastle.util.Selector
+ * @see org.bouncycastle.x509.X509Store
+ * @see org.bouncycastle.jce.provider.X509StoreCRLCollection
+ */
+public class X509CRLStoreSelector
+    extends X509CRLSelector
+    implements Selector
+{
+    private boolean deltaCRLIndicator = false;
+
+    private boolean completeCRLEnabled = false;
+
+    private BigInteger maxBaseCRLNumber = null;
+
+    private byte[] issuingDistributionPoint = null;
+
+    private boolean issuingDistributionPointEnabled = false;
+
+    private X509AttributeCertificate attrCertChecking;
+
+    /**
+     * Returns if the issuing distribution point criteria should be applied.
+     * Defaults to <code>false</code>.
+     * <p>
+     * You may also set the issuing distribution point criteria if not a missing
+     * issuing distribution point should be assumed.
+     * 
+     * @return Returns if the issuing distribution point check is enabled.
+     */
+    public boolean isIssuingDistributionPointEnabled()
+    {
+        return issuingDistributionPointEnabled;
+    }
+
+    /**
+     * Enables or disables the issuing distribution point check.
+     * 
+     * @param issuingDistributionPointEnabled <code>true</code> to enable the
+     *            issuing distribution point check.
+     */
+    public void setIssuingDistributionPointEnabled(
+        boolean issuingDistributionPointEnabled)
+    {
+        this.issuingDistributionPointEnabled = issuingDistributionPointEnabled;
+    }
+
+    /**
+     * Sets the attribute certificate being checked. This is not a criterion.
+     * Rather, it is optional information that may help a {@link X509Store} find
+     * CRLs that would be relevant when checking revocation for the specified
+     * attribute certificate. If <code>null</code> is specified, then no such
+     * optional information is provided.
+     * 
+     * @param attrCert the <code>X509AttributeCertificate</code> being checked (or
+     *            <code>null</code>)
+     * @see #getAttrCertificateChecking()
+     */
+    public void setAttrCertificateChecking(X509AttributeCertificate attrCert)
+    {
+        attrCertChecking = attrCert;
+    }
+
+    /**
+     * Returns the attribute certificate being checked.
+     * 
+     * @return Returns the attribute certificate being checked.
+     * @see #setAttrCertificateChecking(X509AttributeCertificate)
+     */
+    public X509AttributeCertificate getAttrCertificateChecking()
+    {
+        return attrCertChecking;
+    }
+
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509CRL))
+        {
+            return false;
+        }
+        X509CRL crl = (X509CRL)obj;
+        DERInteger dci = null;
+        try
+        {
+            byte[] bytes = crl
+                .getExtensionValue(X509Extensions.DeltaCRLIndicator.getId());
+            if (bytes != null)
+            {
+                dci = DERInteger.getInstance(X509ExtensionUtil
+                    .fromExtensionValue(bytes));
+            }
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+        if (isDeltaCRLIndicatorEnabled())
+        {
+            if (dci == null)
+            {
+                return false;
+            }
+        }
+        if (isCompleteCRLEnabled())
+        {
+            if (dci != null)
+            {
+                return false;
+            }
+        }
+        if (dci != null)
+        {
+
+            if (maxBaseCRLNumber != null)
+            {
+                if (dci.getPositiveValue().compareTo(maxBaseCRLNumber) == 1)
+                {
+                    return false;
+                }
+            }
+        }
+        if (issuingDistributionPointEnabled)
+        {
+            byte[] idp = crl
+                .getExtensionValue(X509Extensions.IssuingDistributionPoint
+                    .getId());
+            if (issuingDistributionPoint == null)
+            {
+                if (idp != null)
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                if (!Arrays.areEqual(idp, issuingDistributionPoint))
+                {
+                    return false;
+                }
+            }
+
+        }
+        return super.match((X509CRL)obj);
+    }
+
+    public boolean match(CRL crl)
+    {
+        return match((Object)crl);
+    }
+
+    /**
+     * Returns if this selector must match CRLs with the delta CRL indicator
+     * extension set. Defaults to <code>false</code>.
+     * 
+     * @return Returns <code>true</code> if only CRLs with the delta CRL
+     *         indicator extension are selected.
+     */
+    public boolean isDeltaCRLIndicatorEnabled()
+    {
+        return deltaCRLIndicator;
+    }
+
+    /**
+     * If this is set to <code>true</code> the CRL reported contains the delta
+     * CRL indicator CRL extension.
+     * <p>
+     * {@link #setCompleteCRLEnabled(boolean)} and
+     * {@link #setDeltaCRLIndicatorEnabled(boolean)} excluded each other.
+     * 
+     * @param deltaCRLIndicator <code>true</code> if the delta CRL indicator
+     *            extension must be in the CRL.
+     */
+    public void setDeltaCRLIndicatorEnabled(boolean deltaCRLIndicator)
+    {
+        this.deltaCRLIndicator = deltaCRLIndicator;
+    }
+
+    /**
+     * Returns an instance of this from a <code>X509CRLSelector</code>.
+     * 
+     * @param selector A <code>X509CRLSelector</code> instance.
+     * @return An instance of an <code>X509CRLStoreSelector</code>.
+     * @exception IllegalArgumentException if selector is null or creation
+     *                fails.
+     */
+    public static X509CRLStoreSelector getInstance(X509CRLSelector selector)
+    {
+        if (selector == null)
+        {
+            throw new IllegalArgumentException(
+                "cannot create from null selector");
+        }
+        X509CRLStoreSelector cs = new X509CRLStoreSelector();
+        cs.setCertificateChecking(selector.getCertificateChecking());
+        cs.setDateAndTime(selector.getDateAndTime());
+        try
+        {
+            cs.setIssuerNames(selector.getIssuerNames());
+        }
+        catch (IOException e)
+        {
+            // cannot happen
+            throw new IllegalArgumentException(e.getMessage());
+        }
+        cs.setIssuers(selector.getIssuers());
+        cs.setMaxCRLNumber(selector.getMaxCRL());
+        cs.setMinCRLNumber(selector.getMinCRL());
+        return cs;
+    }
+    
+    public Object clone()
+    {
+        X509CRLStoreSelector sel = X509CRLStoreSelector.getInstance(this);
+        sel.deltaCRLIndicator = deltaCRLIndicator;
+        sel.completeCRLEnabled = completeCRLEnabled;
+        sel.maxBaseCRLNumber = maxBaseCRLNumber;
+        sel.attrCertChecking = attrCertChecking;
+        sel.issuingDistributionPointEnabled = issuingDistributionPointEnabled;
+        sel.issuingDistributionPoint = Arrays.clone(issuingDistributionPoint);
+        return sel;
+    }
+
+    /**
+     * If <code>true</code> only complete CRLs are returned. Defaults to
+     * <code>false</code>.
+     * 
+     * @return <code>true</code> if only complete CRLs are returned.
+     */
+    public boolean isCompleteCRLEnabled()
+    {
+        return completeCRLEnabled;
+    }
+
+    /**
+     * If set to <code>true</code> only complete CRLs are returned.
+     * <p>
+     * {@link #setCompleteCRLEnabled(boolean)} and
+     * {@link #setDeltaCRLIndicatorEnabled(boolean)} excluded each other.
+     * 
+     * @param completeCRLEnabled <code>true</code> if only complete CRLs
+     *            should be returned.
+     */
+    public void setCompleteCRLEnabled(boolean completeCRLEnabled)
+    {
+        this.completeCRLEnabled = completeCRLEnabled;
+    }
+
+    /**
+     * Get the maximum base CRL number. Defaults to <code>null</code>.
+     * 
+     * @return Returns the maximum base CRL number.
+     * @see #setMaxBaseCRLNumber(BigInteger)
+     */
+    public BigInteger getMaxBaseCRLNumber()
+    {
+        return maxBaseCRLNumber;
+    }
+
+    /**
+     * Sets the maximum base CRL number. Setting to <code>null</code> disables
+     * this cheack.
+     * <p>
+     * This is only meaningful for delta CRLs. Complete CRLs must have a CRL
+     * number which is greater or equal than the base number of the
+     * corresponding CRL.
+     * 
+     * @param maxBaseCRLNumber The maximum base CRL number to set.
+     */
+    public void setMaxBaseCRLNumber(BigInteger maxBaseCRLNumber)
+    {
+        this.maxBaseCRLNumber = maxBaseCRLNumber;
+    }
+
+    /**
+     * Returns the issuing distribution point. Defaults to <code>null</code>,
+     * which is a missing issuing distribution point extension.
+     * <p>
+     * The internal byte array is cloned before it is returned.
+     * <p>
+     * The criteria must be enable with
+     * {@link #setIssuingDistributionPointEnabled(boolean)}.
+     * 
+     * @return Returns the issuing distribution point.
+     * @see #setIssuingDistributionPoint(byte[])
+     */
+    public byte[] getIssuingDistributionPoint()
+    {
+        return Arrays.clone(issuingDistributionPoint);
+    }
+
+    /**
+     * Sets the issuing distribution point.
+     * <p>
+     * The issuing distribution point extension is a CRL extension which
+     * identifies the scope and the distribution point of a CRL. The scope
+     * contains among others information about revocation reasons contained in
+     * the CRL. Delta CRLs and complete CRLs must have matching issuing
+     * distribution points.
+     * <p>
+     * The byte array is cloned to protect against subsequent modifications.
+     * <p>
+     * You must also enable or disable this criteria with
+     * {@link #setIssuingDistributionPointEnabled(boolean)}.
+     * 
+     * @param issuingDistributionPoint The issuing distribution point to set.
+     *            This is the DER encoded OCTET STRING extension value.
+     * @see #getIssuingDistributionPoint()
+     */
+    public void setIssuingDistributionPoint(byte[] issuingDistributionPoint)
+    {
+        this.issuingDistributionPoint = Arrays.clone(issuingDistributionPoint);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/X509CertStoreSelector.java b/src/main/java/org/bouncycastle/x509/X509CertStoreSelector.java
new file mode 100644
index 0000000..b272649
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/X509CertStoreSelector.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.util.Selector;
+
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+
+/**
+ * This class is a Selector implementation for X.509 certificates.
+ * 
+ * @see org.bouncycastle.util.Selector
+ * @see org.bouncycastle.x509.X509Store
+ * @see org.bouncycastle.jce.provider.X509StoreCertCollection
+ */
+public class X509CertStoreSelector
+    extends X509CertSelector
+    implements Selector
+{
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509Certificate))
+        {
+            return false;
+        }
+
+        X509Certificate other = (X509Certificate)obj;
+
+        return super.match(other);
+    }
+
+    public boolean match(Certificate cert)
+    {
+        return match((Object)cert);
+    }
+
+    public Object clone()
+    {
+        X509CertStoreSelector selector = (X509CertStoreSelector)super.clone();
+
+        return selector;
+    }
+
+    /**
+     * Returns an instance of this from a <code>X509CertSelector</code>.
+     *
+     * @param selector A <code>X509CertSelector</code> instance.
+     * @return An instance of an <code>X509CertStoreSelector</code>.
+     * @exception IllegalArgumentException if selector is null or creation fails.
+     */
+    public static X509CertStoreSelector getInstance(X509CertSelector selector)
+    {
+        if (selector == null)
+        {
+            throw new IllegalArgumentException("cannot create from null selector");
+        }
+        X509CertStoreSelector cs = new X509CertStoreSelector();
+        cs.setAuthorityKeyIdentifier(selector.getAuthorityKeyIdentifier());
+        cs.setBasicConstraints(selector.getBasicConstraints());
+        cs.setCertificate(selector.getCertificate());
+        cs.setCertificateValid(selector.getCertificateValid());
+        cs.setMatchAllSubjectAltNames(selector.getMatchAllSubjectAltNames());
+        try
+        {
+            cs.setPathToNames(selector.getPathToNames());
+            cs.setExtendedKeyUsage(selector.getExtendedKeyUsage());
+            cs.setNameConstraints(selector.getNameConstraints());
+            cs.setPolicy(selector.getPolicy());
+            cs.setSubjectPublicKeyAlgID(selector.getSubjectPublicKeyAlgID());
+            cs.setSubjectAlternativeNames(selector.getSubjectAlternativeNames());
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error in passed in selector: " + e);
+        }
+        cs.setIssuer(selector.getIssuer());
+        cs.setKeyUsage(selector.getKeyUsage());
+        cs.setPrivateKeyValid(selector.getPrivateKeyValid());
+        cs.setSerialNumber(selector.getSerialNumber());
+        cs.setSubject(selector.getSubject());
+        cs.setSubjectKeyIdentifier(selector.getSubjectKeyIdentifier());
+        cs.setSubjectPublicKey(selector.getSubjectPublicKey());
+        return cs;
+    }
+
+}
diff --git a/src/main/java/org/bouncycastle/x509/X509Store.java b/src/main/java/org/bouncycastle/x509/X509Store.java
new file mode 100644
index 0000000..1bfc00f
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/X509Store.java
@@ -0,0 +1,79 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.util.Collection;
+
+public class X509Store
+    implements Store
+{
+    public static X509Store getInstance(String type, X509StoreParameters parameters)
+        throws NoSuchStoreException
+    {
+        try
+        {
+            X509Util.Implementation impl = X509Util.getImplementation("X509Store", type);
+
+            return createStore(impl, parameters);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new NoSuchStoreException(e.getMessage());
+        }
+    }
+
+    public static X509Store getInstance(String type, X509StoreParameters parameters, String provider)
+        throws NoSuchStoreException, NoSuchProviderException
+    {
+        return getInstance(type, parameters, X509Util.getProvider(provider));
+    }
+
+    public static X509Store getInstance(String type, X509StoreParameters parameters, Provider provider)
+        throws NoSuchStoreException
+    {
+        try
+        {
+            X509Util.Implementation impl = X509Util.getImplementation("X509Store", type, provider);
+
+            return createStore(impl, parameters);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new NoSuchStoreException(e.getMessage());
+        }
+    }
+
+    private static X509Store createStore(X509Util.Implementation impl, X509StoreParameters parameters)
+    {
+        X509StoreSpi spi = (X509StoreSpi)impl.getEngine();
+
+        spi.engineInit(parameters);
+
+        return new X509Store(impl.getProvider(), spi);
+    }
+
+    private Provider     _provider;
+    private X509StoreSpi _spi;
+
+    private X509Store(
+        Provider provider,
+        X509StoreSpi spi)
+    {
+        _provider = provider;
+        _spi = spi;
+    }
+
+    public Provider getProvider()
+    {
+       return _provider;
+    }
+
+    public Collection getMatches(Selector selector)
+    {
+        return _spi.engineGetMatches(selector);
+    }
+}
diff --git a/src/main/java/org/bouncycastle/x509/X509StoreParameters.java b/src/main/java/org/bouncycastle/x509/X509StoreParameters.java
new file mode 100644
index 0000000..22548da
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/X509StoreParameters.java
@@ -0,0 +1,5 @@
+package org.bouncycastle.x509;
+
+public interface X509StoreParameters
+{
+}
diff --git a/src/main/java/org/bouncycastle/x509/X509StoreSpi.java b/src/main/java/org/bouncycastle/x509/X509StoreSpi.java
new file mode 100644
index 0000000..3455add
--- /dev/null
+++ b/src/main/java/org/bouncycastle/x509/X509StoreSpi.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.x509;
+
+import org.bouncycastle.util.Selector;
+
+import java.util.Collection;
+
+public abstract class X509StoreSpi
+{
+    public abstract void engineInit(X509StoreParameters parameters);
+
+    public abstract Collection engineGetMatches(Selector selector);
+}
diff --git a/src/main/java/org/bouncycastle/x509/X509Util.java b/src/main/java/org/bouncycastle/x509/X509Util.java
index 2130d0e..43b2d90 100644
--- a/src/main/java/org/bouncycastle/x509/X509Util.java
+++ b/src/main/java/org/bouncycastle/x509/X509Util.java
@@ -1,6 +1,32 @@
 package org.bouncycastle.x509;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.util.Strings;
+
+import javax.security.auth.x500.X500Principal;
 import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -9,31 +35,22 @@
 import java.util.List;
 import java.util.Set;
 
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.util.Strings;
-
 class X509Util
 {
     private static Hashtable algorithms = new Hashtable();
+    private static Hashtable params = new Hashtable();
     private static Set       noParams = new HashSet();
     
     static
     {   
-        algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD2WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.2"));
-        algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("MD5WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.4"));
-        algorithms.put("SHA1WITHRSAENCRYPTION", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
-        algorithms.put("SHA1WITHRSA", new DERObjectIdentifier("1.2.840.113549.1.1.5"));
+        // BEGIN android-removed
+        // algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
+        // algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
+        // END android-removed
+        algorithms.put("MD5WITHRSAENCRYPTION", PKCSObjectIdentifiers.md5WithRSAEncryption);
+        algorithms.put("MD5WITHRSA", PKCSObjectIdentifiers.md5WithRSAEncryption);
+        algorithms.put("SHA1WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha1WithRSAEncryption);
+        algorithms.put("SHA1WITHRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption);
         algorithms.put("SHA224WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha224WithRSAEncryption);
         algorithms.put("SHA224WITHRSA", PKCSObjectIdentifiers.sha224WithRSAEncryption);
         algorithms.put("SHA256WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha256WithRSAEncryption);
@@ -42,8 +59,17 @@
         algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
         algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
         algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
-        algorithms.put("RIPEMD160WITHRSAENCRYPTION", new DERObjectIdentifier("1.3.36.3.3.1.2"));
-        algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier("1.3.36.3.3.1.2"));
+        algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        algorithms.put("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        algorithms.put("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        algorithms.put("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
         algorithms.put("SHA1WITHDSA", X9ObjectIdentifiers.id_dsa_with_sha1);
         algorithms.put("DSAWITHSHA1", X9ObjectIdentifiers.id_dsa_with_sha1);
         algorithms.put("SHA224WITHDSA", NISTObjectIdentifiers.dsa_with_sha224);
@@ -56,7 +82,10 @@
         algorithms.put("SHA512WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512);
         algorithms.put("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         algorithms.put("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
-        
+        algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+
         //
         // 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.
@@ -69,8 +98,51 @@
         noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
+
+        //
+        // RFC 4491
+        //
+        noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
+        noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+
+        //
+        // explicit params
+        //
+        // BEGIN android-changed
+        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA1WITHRSAANDMGF1", creatPSSParams(sha1AlgId, 20));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA224WITHRSAANDMGF1", creatPSSParams(sha224AlgId, 28));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA256WITHRSAANDMGF1", creatPSSParams(sha256AlgId, 32));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA384WITHRSAANDMGF1", creatPSSParams(sha384AlgId, 48));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA512WITHRSAANDMGF1", creatPSSParams(sha512AlgId, 64));
     }
-     
+
+    private static RSASSAPSSparams creatPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
+    {
+        return new RSASSAPSSparams(
+            hashAlgId,
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId),
+            new DERInteger(saltSize),
+            new DERInteger(1));
+    }
+
     static DERObjectIdentifier getAlgorithmOID(
         String algorithmName)
     {
@@ -85,16 +157,24 @@
     }
     
     static AlgorithmIdentifier getSigAlgID(
-        DERObjectIdentifier sigOid)
+        DERObjectIdentifier sigOid,
+        String              algorithmName)
     {
         if (noParams.contains(sigOid))
         {
             return new AlgorithmIdentifier(sigOid);
         }
+
+        algorithmName = Strings.toUpperCase(algorithmName);
+
+        if (params.containsKey(algorithmName))
+        {
+            return new AlgorithmIdentifier(sigOid, (DEREncodable)params.get(algorithmName));
+        }
         else
         {
             // BEGIN android-changed
-            return new AlgorithmIdentifier(sigOid, DERNull.THE_ONE);
+            return new AlgorithmIdentifier(sigOid, DERNull.INSTANCE);
             // END android-changed
         }
     }
@@ -112,6 +192,91 @@
         return l.iterator();
     }
 
+    static Signature getSignatureInstance(
+        String algorithm)
+        throws NoSuchAlgorithmException
+    {
+        return Signature.getInstance(algorithm);
+    }
+
+    static Signature getSignatureInstance(
+        String algorithm,
+        String provider)
+        throws NoSuchProviderException, NoSuchAlgorithmException
+    {
+        if (provider != null)
+        {
+            return Signature.getInstance(algorithm, provider);
+        }
+        else
+        {
+            return Signature.getInstance(algorithm);
+        }
+    }
+
+    static byte[] calculateSignature(
+        DERObjectIdentifier sigOid,
+        String              sigName,
+        PrivateKey          key,
+        SecureRandom        random,
+        ASN1Encodable       object)
+        throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException
+    {
+        Signature sig;
+
+        if (sigOid == null)
+        {
+            throw new IllegalStateException("no signature algorithm specified");
+        }
+
+        sig = X509Util.getSignatureInstance(sigName);
+
+        if (random != null)
+        {
+            sig.initSign(key, random);
+        }
+        else
+        {
+            sig.initSign(key);
+        }
+
+        sig.update(object.getEncoded(ASN1Encodable.DER));
+
+        return sig.sign();
+    }
+
+    static byte[] calculateSignature(
+        DERObjectIdentifier sigOid,
+        String              sigName,
+        String              provider,
+        PrivateKey          key,
+        SecureRandom        random,
+        ASN1Encodable       object)
+        throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, SignatureException
+    {
+        Signature sig;
+
+        if (sigOid == null)
+        {
+            throw new IllegalStateException("no signature algorithm specified");
+        }
+
+        sig = X509Util.getSignatureInstance(sigName, provider);
+
+        if (random != null)
+        {
+            sig.initSign(key, random);
+        }
+        else
+        {
+            sig.initSign(key);
+        }
+
+        sig.update(object.getEncoded(ASN1Encodable.DER));
+
+        return sig.sign();
+    }
+
     static X509Principal convertPrincipal(
         X500Principal principal)
     {
@@ -124,4 +289,133 @@
             throw new IllegalArgumentException("cannot convert principal");
         }
     }
+
+    static class Implementation
+    {
+        Object      engine;
+        Provider provider;
+
+        Implementation(
+            Object      engine,
+            Provider    provider)
+        {
+            this.engine = engine;
+            this.provider = provider;
+        }
+
+        Object getEngine()
+        {
+            return engine;
+        }
+
+        Provider getProvider()
+        {
+            return provider;
+        }
+    }
+
+    /**
+     * see if we can find an algorithm (or its alias and what it represents) in
+     * the property table for the given provider.
+     */
+    static Implementation getImplementation(
+        String      baseName,
+        String      algorithm,
+        Provider    prov)
+        throws NoSuchAlgorithmException
+    {
+        algorithm = Strings.toUpperCase(algorithm);
+
+        String      alias;
+
+        while ((alias = prov.getProperty("Alg.Alias." + baseName + "." + algorithm)) != null)
+        {
+            algorithm = alias;
+        }
+
+        String      className = prov.getProperty(baseName + "." + algorithm);
+
+        if (className != null)
+        {
+            try
+            {
+                Class       cls;
+                ClassLoader clsLoader = prov.getClass().getClassLoader();
+
+                if (clsLoader != null)
+                {
+                    cls = clsLoader.loadClass(className);
+                }
+                else
+                {
+                    cls = Class.forName(className);
+                }
+
+                return new Implementation(cls.newInstance(), prov);
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new IllegalStateException(
+                    "algorithm " + algorithm + " in provider " + prov.getName() + " but no class \"" + className + "\" found!");
+            }
+            catch (Exception e)
+            {
+                throw new IllegalStateException(
+                    "algorithm " + algorithm + " in provider " + prov.getName() + " but class \"" + className + "\" inaccessible!");
+            }
+        }
+
+        throw new NoSuchAlgorithmException("cannot find implementation " + algorithm + " for provider " + prov.getName());
+    }
+
+    /**
+     * return an implementation for a given algorithm/provider.
+     * If the provider is null, we grab the first avalaible who has the required algorithm.
+     */
+    static Implementation getImplementation(
+        String      baseName,
+        String      algorithm)
+        throws NoSuchAlgorithmException
+    {
+        Provider[] prov = Security.getProviders();
+
+        //
+        // search every provider looking for the algorithm we want.
+        //
+        for (int i = 0; i != prov.length; i++)
+        {
+            //
+            // try case insensitive
+            //
+            Implementation imp = getImplementation(baseName, Strings.toUpperCase(algorithm), prov[i]);
+            if (imp != null)
+            {
+                return imp;
+            }
+
+            try
+            {
+                imp = getImplementation(baseName, algorithm, prov[i]);
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                // continue
+            }
+        }
+
+        throw new NoSuchAlgorithmException("cannot find implementation " + algorithm);
+    }
+
+    static Provider getProvider(String provider)
+        throws NoSuchProviderException
+    {
+        Provider prov = Security.getProvider(provider);
+
+        if (prov == null)
+        {
+            throw new NoSuchProviderException("Provider " + provider + " not found");
+        }
+
+        return prov;
+    }
 }
diff --git a/src/main/java/org/bouncycastle/x509/X509V1CertificateGenerator.java b/src/main/java/org/bouncycastle/x509/X509V1CertificateGenerator.java
deleted file mode 100644
index 09531b6..0000000
--- a/src/main/java/org/bouncycastle/x509/X509V1CertificateGenerator.java
+++ /dev/null
@@ -1,301 +0,0 @@
-package org.bouncycastle.x509;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Iterator;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.TBSCertificateStructure;
-import org.bouncycastle.asn1.x509.Time;
-import org.bouncycastle.asn1.x509.V1TBSCertificateGenerator;
-import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-
-/**
- * class to produce an X.509 Version 1 certificate.
- */
-public class X509V1CertificateGenerator
-{
-    private V1TBSCertificateGenerator   tbsGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-
-    public X509V1CertificateGenerator()
-    {
-        tbsGen = new V1TBSCertificateGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        tbsGen = new V1TBSCertificateGenerator();
-    }
-
-    /**
-     * set the serial number for the certificate.
-     */
-    public void setSerialNumber(
-        BigInteger      serialNumber)
-    {
-        if (serialNumber.compareTo(BigInteger.ZERO) <= 0)
-        {
-            throw new IllegalArgumentException("serial number must be a positive integer");
-        }
-        
-        tbsGen.setSerialNumber(new DERInteger(serialNumber));
-    }
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X500Principal   issuer)
-    {
-        try
-        {
-            tbsGen.setIssuer(new X509Principal(issuer.getEncoded()));
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("can't process principal: " + e);
-        }
-    }
-    
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X509Name   issuer)
-    {
-        tbsGen.setIssuer(issuer);
-    }
-
-    public void setNotBefore(
-        Date    date)
-    {
-        tbsGen.setStartDate(new Time(date));
-    }
-
-    public void setNotAfter(
-        Date    date)
-    {
-        tbsGen.setEndDate(new Time(date));
-    }
-
-    /**
-     * Set the subject distinguished name. The subject describes the entity associated with the public key.
-     */
-    public void setSubjectDN(
-        X500Principal   subject)
-    {
-        try
-        {
-            tbsGen.setSubject(new X509Principal(subject.getEncoded()));
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("can't process principal: " + e);
-        }
-    }
-    
-    /**
-     * Set the subject distinguished name. The subject describes the entity associated with the public key.
-     */
-    public void setSubjectDN(
-        X509Name   subject)
-    {
-        tbsGen.setSubject(subject);
-    }
-
-    public void setPublicKey(
-        PublicKey       key)
-    {
-        try
-        {
-            tbsGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
-                                new ByteArrayInputStream(key.getEncoded())).readObject()));
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("unable to process key - " + e.toString());
-        }
-    }
-
-    /**
-     * Set the signature algorithm. This can be either a name or an OID, names
-     * are treated as case insensitive.
-     * 
-     * @param signatureAlgorithm string representation of the algorithm name.
-     */
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        try
-        {
-            sigOID = X509Util.getAlgorithmOID(signatureAlgorithm);
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        sigAlgId = X509Util.getSigAlgID(sigOID);
-
-        tbsGen.setSignature(sigAlgId);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC".
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", null);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the default provider "BC" and the passed in source of randomness
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        SecureRandom    random)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509Certificate(key, "BC", random);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing, and the passed in source
-     * of randomness (if required).
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateX509Certificate(key, provider, null);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing, and the passed in source
-     * of randomness (if required).
-     */
-    public X509Certificate generateX509Certificate(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        TBSCertificateStructure tbsCert = tbsGen.generateTBSCertificate();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCert);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding TBS cert - " + e);
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(tbsCert);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        return new X509CertificateObject(new X509CertificateStructure(new DERSequence(v)));
-    }
-    
-    /**
-     * Return an iterator of the signature names supported by the generator.
-     * 
-     * @return an iterator containing recognised names.
-     */
-    public Iterator getSignatureAlgNames()
-    {
-        return X509Util.getAlgNames();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java b/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
index 7348b87..e91e8ff 100644
--- a/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
+++ b/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
@@ -1,7 +1,16 @@
 package org.bouncycastle.x509;
 
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.util.Arrays;
+
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
@@ -22,17 +31,7 @@
 import java.util.List;
 import java.util.Set;
 
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.x509.AttributeCertificate;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-/*
+/**
  * An implementation of a version 2 X.509 Attribute Certificate.
  */
 public class X509V2AttributeCertificate
@@ -75,7 +74,7 @@
     
     public int getVersion()
     {
-        return cert.getAcinfo().getVersion().getValue().intValue();
+        return cert.getAcinfo().getVersion().getValue().intValue() + 1;
     }
     
     public BigInteger getSerialNumber()
@@ -197,14 +196,9 @@
 
             if (ext != null)
             {
-                ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
-                DEROutputStream            dOut = new DEROutputStream(bOut);
-                
                 try
                 {
-                    dOut.writeObject(ext.getValue());
-
-                    return bOut.toByteArray();
+                    return ext.getValue().getEncoded(ASN1Encodable.DER);
                 }
                 catch (Exception e)
                 {
@@ -294,4 +288,44 @@
         
         return (X509Attribute[])list.toArray(new X509Attribute[list.size()]);
     }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof X509AttributeCertificate))
+        {
+            return false;
+        }
+
+        X509AttributeCertificate other = (X509AttributeCertificate)o;
+
+        try
+        {
+            byte[] b1 = this.getEncoded();
+            byte[] b2 = other.getEncoded();
+
+            return Arrays.areEqual(b1, b2);
+        }
+        catch (IOException e)
+        {
+            return false;
+        }
+    }
+
+    public int hashCode()
+    {
+        try
+        {
+            return Arrays.hashCode(this.getEncoded());
+        }
+        catch (IOException e)
+        {
+            return 0;
+        }
+    }
 }
diff --git a/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificateGenerator.java b/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificateGenerator.java
deleted file mode 100644
index ec1a0d7..0000000
--- a/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificateGenerator.java
+++ /dev/null
@@ -1,283 +0,0 @@
-package org.bouncycastle.x509;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Vector;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.AttCertIssuer;
-import org.bouncycastle.asn1.x509.Attribute;
-import org.bouncycastle.asn1.x509.AttributeCertificate;
-import org.bouncycastle.asn1.x509.V2AttributeCertificateInfoGenerator;
-import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-
-/**
- * class to produce an X.509 Version 2 AttributeCertificate.
- */
-public class X509V2AttributeCertificateGenerator
-{
-    private V2AttributeCertificateInfoGenerator   acInfoGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-    private Hashtable                   extensions = null;
-    private Vector                      extOrdering = null;
-
-    public X509V2AttributeCertificateGenerator()
-    {
-        acInfoGen = new V2AttributeCertificateInfoGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        acInfoGen = new V2AttributeCertificateInfoGenerator();
-        extensions = null;
-        extOrdering = null;
-    }
-
-    /**
-     * Set the Holder of this Attribute Certificate
-     */
-    public void setHolder(
-        AttributeCertificateHolder     holder)
-    {
-        acInfoGen.setHolder(holder.holder);
-    }
-
-    /**
-     * Set the issuer
-     */
-    public void setIssuer(
-        AttributeCertificateIssuer  issuer)
-    {
-        acInfoGen.setIssuer(AttCertIssuer.getInstance(issuer.form));
-    }
-
-    /**
-     * set the serial number for the certificate.
-     */
-    public void setSerialNumber(
-        BigInteger      serialNumber)
-    {
-        acInfoGen.setSerialNumber(new DERInteger(serialNumber));
-    }
-
-    public void setNotBefore(
-        Date    date)
-    {
-        acInfoGen.setStartDate(new DERGeneralizedTime(date));
-    }
-
-    public void setNotAfter(
-        Date    date)
-    {
-        acInfoGen.setEndDate(new DERGeneralizedTime(date));
-    }
-
-    /**
-     * Set the signature algorithm. This can be either a name or an OID, names
-     * are treated as case insensitive.
-     * 
-     * @param signatureAlgorithm string representation of the algorithm name.
-     */
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        try
-        {
-            sigOID = X509Util.getAlgorithmOID(signatureAlgorithm);
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        // BEGIN android-changed
-        sigAlgId = new AlgorithmIdentifier(this.sigOID, DERNull.THE_ONE);
-        // END android-changed
-
-        acInfoGen.setSignature(sigAlgId);
-    }
-    
-    /**
-     * add an attribute
-     */
-    public void addAttribute(
-        X509Attribute       attribute)
-    {
-        acInfoGen.addAttribute(Attribute.getInstance(attribute.toASN1Object()));
-    }
-
-    public void setIssuerUniqueId(
-        boolean[] iui)
-    {
-        // [TODO] convert boolean array to bit string
-        //acInfoGen.setIssuerUniqueID(iui);
-        throw new RuntimeException("not implemented (yet)");
-    }
-     
-    /**
-     * add a given extension field for the standard extensions tag
-     * @throws IOException
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        ASN1Encodable   value)
-        throws IOException
-    {
-        this.addExtension(OID, critical, value.getEncoded());
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag
-     * The value parameter becomes the contents of the octet string associated
-     * with the extension.
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        byte[]          value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        DERObjectIdentifier oid = new DERObjectIdentifier(OID);
-        
-        extensions.put(oid, new X509Extension(critical, new DEROctetString(value)));
-        extOrdering.addElement(oid);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing.
-     */
-    public X509AttributeCertificate generateCertificate(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateCertificate(key, provider, null);
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject,
-     * using the passed in provider for the signing and the supplied source
-     * of randomness, if required.
-     */
-    public X509AttributeCertificate generateCertificate(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        if (sigOID == null)
-        {
-            throw new IllegalStateException("no signature algorithm specified");
-        }
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        if (extensions != null)
-        {
-            acInfoGen.setExtensions(new X509Extensions(extOrdering, extensions));
-        }
-
-        AttributeCertificateInfo acInfo = acInfoGen.generateAttributeCertificateInfo();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(acInfo);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding Attribute cert - " + e);
-        }
-
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(acInfo);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        try
-        {
-            return new X509V2AttributeCertificate(new AttributeCertificate(new DERSequence(v)));
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException("constructed invalid certificate!");
-        }
-    }
-    
-    /**
-     * Return an iterator of the signature names supported by the generator.
-     * 
-     * @return an iterator containing recognised names.
-     */
-    public Iterator getSignatureAlgNames()
-    {
-        return X509Util.getAlgNames();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/X509V2CRLGenerator.java b/src/main/java/org/bouncycastle/x509/X509V2CRLGenerator.java
deleted file mode 100644
index f639be6..0000000
--- a/src/main/java/org/bouncycastle/x509/X509V2CRLGenerator.java
+++ /dev/null
@@ -1,400 +0,0 @@
-package org.bouncycastle.x509;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CRLException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLEntry;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SimpleTimeZone;
-import java.util.Vector;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DEREncodable;
-import org.bouncycastle.asn1.DERGeneralizedTime;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.CertificateList;
-import org.bouncycastle.asn1.x509.TBSCertList;
-import org.bouncycastle.asn1.x509.Time;
-import org.bouncycastle.asn1.x509.V2TBSCertListGenerator;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.asn1.x509.X509Name;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.X509CRLObject;
-
-/**
- * class to produce an X.509 Version 2 CRL.
- */
-public class X509V2CRLGenerator
-{
-    private SimpleDateFormat            dateF = new SimpleDateFormat("yyMMddHHmmss");
-    private SimpleTimeZone              tz = new SimpleTimeZone(0, "Z");
-    private V2TBSCertListGenerator      tbsGen;
-    private DERObjectIdentifier         sigOID;
-    private AlgorithmIdentifier         sigAlgId;
-    private String                      signatureAlgorithm;
-    private Hashtable                   extensions = null;
-    private Vector                      extOrdering = null;
-
-    public X509V2CRLGenerator()
-    {
-        dateF.setTimeZone(tz);
-
-        tbsGen = new V2TBSCertListGenerator();
-    }
-
-    /**
-     * reset the generator
-     */
-    public void reset()
-    {
-        tbsGen = new V2TBSCertListGenerator();
-    }
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X500Principal   issuer)
-    {
-        try
-        {
-            tbsGen.setIssuer(new X509Principal(issuer.getEncoded()));
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("can't process principal: " + e);
-        }
-    }
-
-    /**
-     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
-     * certificate.
-     */
-    public void setIssuerDN(
-        X509Name   issuer)
-    {
-        tbsGen.setIssuer(issuer);
-    }
-
-    public void setThisUpdate(
-        Date    date)
-    {
-        tbsGen.setThisUpdate(new Time(date));
-    }
-
-    public void setNextUpdate(
-        Date    date)
-    {
-        tbsGen.setNextUpdate(new Time(date));
-    }
-
-    /**
-     * Reason being as indicated by ReasonFlags, i.e. ReasonFlags.keyCompromise
-     * or 0 if ReasonFlags are not to be used
-     **/
-    public void addCRLEntry(BigInteger userCertificate, Date revocationDate, int reason)
-    {
-        tbsGen.addCRLEntry(new DERInteger(userCertificate), new Time(revocationDate), reason);
-    }
-
-    /**
-     * Add a CRL entry with an Invalidity Date extension as well as a CRLReason extension.
-     * Reason being as indicated by ReasonFlags, i.e. ReasonFlags.keyCompromise
-     * or 0 if ReasonFlags are not to be used
-     **/
-    public void addCRLEntry(BigInteger userCertificate, Date revocationDate, int reason, Date invalidityDate)
-    {
-        tbsGen.addCRLEntry(new DERInteger(userCertificate), new Time(revocationDate), reason, new DERGeneralizedTime(invalidityDate));
-    }
-   
-    /**
-     * Add a CRL entry with extensions.
-     **/
-    public void addCRLEntry(BigInteger userCertificate, Date revocationDate, X509Extensions extensions)
-    {
-        tbsGen.addCRLEntry(new DERInteger(userCertificate), new Time(revocationDate), extensions);
-    }
-    
-    /**
-     * Add the CRLEntry objects contained in a previous CRL.
-     * 
-     * @param other the X509CRL to source the other entries from. 
-     */
-    public void addCRL(X509CRL other)
-        throws CRLException
-    {
-        Set revocations = other.getRevokedCertificates();
-        
-        Iterator it = revocations.iterator();
-        while (it.hasNext())
-        {
-            X509CRLEntry entry = (X509CRLEntry)it.next();
-            
-            ASN1InputStream aIn = new ASN1InputStream(entry.getEncoded());
-            
-            try
-            {
-                tbsGen.addCRLEntry(ASN1Sequence.getInstance(aIn.readObject()));
-            }
-            catch (IOException e)
-            {
-                throw new CRLException("exception processing encoding of CRL: " + e.toString());
-            }
-        }
-    }
-    
-    /**
-     * Set the signature algorithm. This can be either a name or an OID, names
-     * are treated as case insensitive.
-     * 
-     * @param signatureAlgorithm string representation of the algorithm name.
-     */
-    public void setSignatureAlgorithm(
-        String  signatureAlgorithm)
-    {
-        this.signatureAlgorithm = signatureAlgorithm;
-
-        try
-        {
-            sigOID = X509Util.getAlgorithmOID(signatureAlgorithm);
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested");
-        }
-
-        sigAlgId = X509Util.getSigAlgID(sigOID);
-
-        tbsGen.setSignature(sigAlgId);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        DEREncodable    value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        DEREncodable        value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
-        try
-        {
-            dOut.writeObject(value);
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("error encoding value: " + e);
-        }
-
-        this.addExtension(OID, critical, bOut.toByteArray());
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        String          OID,
-        boolean         critical,
-        byte[]          value)
-    {
-        this.addExtension(new DERObjectIdentifier(OID), critical, value);
-    }
-
-    /**
-     * add a given extension field for the standard extensions tag (tag 0)
-     */
-    public void addExtension(
-        DERObjectIdentifier OID,
-        boolean             critical,
-        byte[]              value)
-    {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        extensions.put(OID, new X509Extension(critical, new DEROctetString(value)));
-        extOrdering.addElement(OID);
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject
-     * using the default provider "BC".
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509CRL(key, "BC", null);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject
-     * using the default provider "BC" and an user defined SecureRandom object as
-     * source of randomness.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        SecureRandom    random)
-        throws SecurityException, SignatureException, InvalidKeyException
-    {
-        try
-        {
-            return generateX509CRL(key, "BC", random);
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new SecurityException("BC provider not installed!");
-        }
-    }
-
-    /**
-     * generate an X509 certificate, based on the current issuer and subject
-     * using the passed in provider for the signing.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        String          provider)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        return generateX509CRL(key, provider, null);
-    }
-
-    /**
-     * generate an X509 CRL, based on the current issuer and subject,
-     * using the passed in provider for the signing.
-     */
-    public X509CRL generateX509CRL(
-        PrivateKey      key,
-        String          provider,
-        SecureRandom    random)
-        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
-    {
-        Signature sig = null;
-
-        try
-        {
-            sig = Signature.getInstance(sigOID.getId(), provider);
-        }
-        catch (NoSuchAlgorithmException ex)
-        {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
-        }
-
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
-
-        if (extensions != null)
-        {
-            tbsGen.setExtensions(new X509Extensions(extOrdering, extensions));
-        }
-
-        TBSCertList tbsCrl = tbsGen.generateTBSCertList();
-
-        try
-        {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCrl);
-
-            sig.update(bOut.toByteArray());
-        }
-        catch (Exception e)
-        {
-            throw new SecurityException("exception encoding TBS cert - " + e);
-        }
-
-        // Construct the CRL
-        ASN1EncodableVector  v = new ASN1EncodableVector();
-
-        v.add(tbsCrl);
-        v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
-
-        try
-        {
-            return new X509CRLObject(new CertificateList(new DERSequence(v)));
-        }
-        catch (CRLException e)
-        {
-            throw new SecurityException("exception creating CRL: " + e.getMessage());
-        }
-    }
-    
-    
-    /**
-     * Return an iterator of the signature names supported by the generator.
-     * 
-     * @return an iterator containing recognised names.
-     */
-    public Iterator getSignatureAlgNames()
-    {
-        return X509Util.getAlgNames();
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/X509V3CertificateGenerator.java b/src/main/java/org/bouncycastle/x509/X509V3CertificateGenerator.java
index acfa0b4..efe024d 100644
--- a/src/main/java/org/bouncycastle/x509/X509V3CertificateGenerator.java
+++ b/src/main/java/org/bouncycastle/x509/X509V3CertificateGenerator.java
@@ -1,40 +1,42 @@
 package org.bouncycastle.x509;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Vector;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.TBSCertificateStructure;
 import org.bouncycastle.asn1.x509.Time;
 import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
 import org.bouncycastle.asn1.x509.X509CertificateStructure;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.X509ExtensionsGenerator;
 import org.bouncycastle.asn1.x509.X509Name;
 import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.provider.X509CertificateObject;
 import org.bouncycastle.x509.extension.X509ExtensionUtil;
 
+import javax.security.auth.x500.X500Principal;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Iterator;
+
 /**
  * class to produce an X.509 Version 3 certificate.
  */
@@ -44,12 +46,12 @@
     private DERObjectIdentifier         sigOID;
     private AlgorithmIdentifier         sigAlgId;
     private String                      signatureAlgorithm;
-    private Hashtable                   extensions = null;
-    private Vector                      extOrdering = null;
+    private X509ExtensionsGenerator     extGenerator;
 
     public X509V3CertificateGenerator()
     {
         tbsGen = new V3TBSCertificateGenerator();
+        extGenerator = new X509ExtensionsGenerator();
     }
 
     /**
@@ -58,8 +60,7 @@
     public void reset()
     {
         tbsGen = new V3TBSCertificateGenerator();
-        extensions = null;
-        extOrdering = null;
+        extGenerator.reset();
     }
 
     /**
@@ -142,11 +143,12 @@
 
     public void setPublicKey(
         PublicKey       key)
+        throws IllegalArgumentException
     {
         try
         {
-            tbsGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
-                                new ByteArrayInputStream(key.getEncoded())).readObject()));
+            tbsGen.setSubjectPublicKeyInfo(
+                       SubjectPublicKeyInfo.getInstance(new ASN1InputStream(key.getEncoded()).readObject()));
         }
         catch (Exception e)
         {
@@ -174,12 +176,49 @@
             throw new IllegalArgumentException("Unknown signature type requested: " + signatureAlgorithm);
         }
 
-        sigAlgId = X509Util.getSigAlgID(sigOID);
+        sigAlgId = X509Util.getSigAlgID(sigOID, signatureAlgorithm);
 
         tbsGen.setSignature(sigAlgId);
     }
 
     /**
+     * Set the subject unique ID - note: it is very rare that it is correct to do this.
+     */
+    public void setSubjectUniqueID(boolean[] uniqueID)
+    {
+        tbsGen.setSubjectUniqueID(booleanToBitString(uniqueID));
+    }
+
+    /**
+     * Set the issuer unique ID - note: it is very rare that it is correct to do this.
+     */
+    public void setIssuerUniqueID(boolean[] uniqueID)
+    {
+        tbsGen.setIssuerUniqueID(booleanToBitString(uniqueID));
+    }
+
+    private DERBitString booleanToBitString(boolean[] id)
+    {
+        byte[] bytes = new byte[(id.length + 7) / 8];
+
+        for (int i = 0; i != id.length; i++)
+        {
+            bytes[i / 8] |= (id[i]) ? (1 << ((7 - (i % 8)))) : 0;
+        }
+
+        int pad = id.length % 8;
+
+        if (pad == 0)
+        {
+            return new DERBitString(bytes);
+        }
+        else
+        {
+            return new DERBitString(bytes, 8 - pad);
+        }
+    }
+    
+    /**
      * add a given extension field for the standard extensions tag (tag 3)
      */
     public void addExtension(
@@ -198,25 +237,7 @@
         boolean             critical,
         DEREncodable        value)
     {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-        DEROutputStream         dOut = new DEROutputStream(bOut);
-
-        try
-        {
-            dOut.writeObject(value);
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("error encoding value: " + e);
-        }
-
-        this.addExtension(oid, critical, bOut.toByteArray());
+        extGenerator.addExtension(oid, critical,  value);
     }
 
     /**
@@ -240,14 +261,7 @@
         boolean             critical,
         byte[]              value)
     {
-        if (extensions == null)
-        {
-            extensions = new Hashtable();
-            extOrdering = new Vector();
-        }
-
-        extensions.put(oid, new X509Extension(critical, new DEROctetString(value)));
-        extOrdering.addElement(oid);
+        extGenerator.addExtension(oid, critical, value);
     }
 
     /**
@@ -297,6 +311,7 @@
     /**
      * generate an X509 certificate, based on the current issuer and subject
      * using the default provider "BC".
+     * @deprecated use generate(key, "BC")
      */
     public X509Certificate generateX509Certificate(
         PrivateKey      key)
@@ -316,6 +331,7 @@
      * generate an X509 certificate, based on the current issuer and subject
      * using the default provider "BC", and the passed in source of randomness
      * (if required).
+     * @deprecated use generate(key, random, "BC")
      */
     public X509Certificate generateX509Certificate(
         PrivateKey      key,
@@ -335,6 +351,7 @@
     /**
      * generate an X509 certificate, based on the current issuer and subject,
      * using the passed in provider for the signing.
+     * @deprecated use generate()
      */
     public X509Certificate generateX509Certificate(
         PrivateKey      key,
@@ -348,6 +365,7 @@
      * generate an X509 certificate, based on the current issuer and subject,
      * using the passed in provider for the signing and the supplied source
      * of randomness, if required.
+     * @deprecated use generate()
      */
     public X509Certificate generateX509Certificate(
         PrivateKey      key,
@@ -355,68 +373,146 @@
         SecureRandom    random)
         throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
     {
-        Signature sig = null;
-
-        if (sigOID == null)
+        try
         {
-            throw new IllegalStateException("no signature algorithm specified");
+            return generate(key, provider, random);
+        }
+        catch (NoSuchProviderException e)
+        {
+            throw e;
+        }
+        catch (SignatureException e)
+        {
+            throw e;
+        }
+        catch (InvalidKeyException e)
+        {
+            throw e;
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new SecurityException("exception: " + e);
+        }
+    }
+
+    /**
+     * generate an X509 certificate, based on the current issuer and subject
+     * using the default provider.
+     * <p>
+     * <b>Note:</b> this differs from the deprecated method in that the default provider is
+     * used - not "BC".
+     * </p>
+     */
+    public X509Certificate generate(
+        PrivateKey      key)
+        throws CertificateEncodingException, IllegalStateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
+    {
+        return generate(key, (SecureRandom)null);
+    }
+
+    /**
+     * generate an X509 certificate, based on the current issuer and subject
+     * using the default provider, and the passed in source of randomness
+     * (if required).
+     * <p>
+     * <b>Note:</b> this differs from the deprecated method in that the default provider is
+     * used - not "BC".
+     * </p>
+     */
+    public X509Certificate generate(
+        PrivateKey      key,
+        SecureRandom    random)
+        throws CertificateEncodingException, IllegalStateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
+    {
+        TBSCertificateStructure tbsCert = generateTbsCert();
+        byte[] signature;
+
+        try
+        {
+            signature = X509Util.calculateSignature(sigOID, signatureAlgorithm, key, random, tbsCert);
+        }
+        catch (IOException e)
+        {
+            throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
         }
 
         try
         {
-            sig = Signature.getInstance(sigOID.getId(), provider);
+            return generateJcaObject(tbsCert, signature);
         }
-        catch (NoSuchAlgorithmException ex)
+        catch (CertificateParsingException e)
         {
-            try
-            {
-                sig = Signature.getInstance(signatureAlgorithm, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                throw new SecurityException("exception creating signature: " + e.toString());
-            }
+            throw new ExtCertificateEncodingException("exception producing certificate object", e);
         }
+    }
 
-        if (random != null)
-        {
-            sig.initSign(key, random);
-        }
-        else
-        {
-            sig.initSign(key);
-        }
+    /**
+     * generate an X509 certificate, based on the current issuer and subject,
+     * using the passed in provider for the signing.
+     */
+    public X509Certificate generate(
+        PrivateKey      key,
+        String          provider)
+        throws CertificateEncodingException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
+    {
+        return generate(key, provider, null);
+    }
 
-        if (extensions != null)
-        {
-            tbsGen.setExtensions(new X509Extensions(extOrdering, extensions));
-        }
-
-        TBSCertificateStructure tbsCert = tbsGen.generateTBSCertificate();
+    /**
+     * generate an X509 certificate, based on the current issuer and subject,
+     * using the passed in provider for the signing and the supplied source
+     * of randomness, if required.
+     */
+    public X509Certificate generate(
+        PrivateKey      key,
+        String          provider,
+        SecureRandom    random)
+        throws CertificateEncodingException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
+    {
+        TBSCertificateStructure tbsCert = generateTbsCert();
+        byte[] signature;
 
         try
         {
-            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-            DEROutputStream         dOut = new DEROutputStream(bOut);
-
-            dOut.writeObject(tbsCert);
-
-            sig.update(bOut.toByteArray());
+            signature = X509Util.calculateSignature(sigOID, signatureAlgorithm, provider, key, random, tbsCert);
         }
-        catch (Exception e)
+        catch (IOException e)
         {
-            throw new SecurityException("exception encoding TBS cert - " + e);
+            throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
         }
 
-        ASN1EncodableVector  v = new ASN1EncodableVector();
+        try
+        {
+            return generateJcaObject(tbsCert, signature);
+        }
+        catch (CertificateParsingException e)
+        {
+            throw new ExtCertificateEncodingException("exception producing certificate object", e);
+        }
+    }
+
+    private TBSCertificateStructure generateTbsCert()
+    {
+        if (!extGenerator.isEmpty())
+        {
+            tbsGen.setExtensions(extGenerator.generate());
+        }
+
+        return tbsGen.generateTBSCertificate();
+    }
+
+    private X509Certificate generateJcaObject(TBSCertificateStructure tbsCert, byte[] signature)
+        throws CertificateParsingException
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
 
         v.add(tbsCert);
         v.add(sigAlgId);
-        v.add(new DERBitString(sig.sign()));
+        v.add(new DERBitString(signature));
 
         return new X509CertificateObject(new X509CertificateStructure(new DERSequence(v)));
     }
-    
+
     /**
      * Return an iterator of the signature names supported by the generator.
      * 
diff --git a/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java b/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java
deleted file mode 100644
index 2280198..0000000
--- a/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package org.bouncycastle.x509.extension;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.PublicKey;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.X509Extensions;
-import org.bouncycastle.jce.PrincipalUtil;
-
-/**
- * A high level authority key identifier.
- */
-public class AuthorityKeyIdentifierStructure
-    extends AuthorityKeyIdentifier
-{
-    /**
-     * Constructor which will take the byte[] returned from getExtensionValue()
-     * 
-     * @param encodedValue a DER octet encoded string with the extension structure in it.
-     * @throws IOException on parsing errors.
-     */
-    public AuthorityKeyIdentifierStructure(
-        byte[]  encodedValue)
-        throws IOException
-    {
-        super((ASN1Sequence)X509ExtensionUtil.fromExtensionValue(encodedValue));
-    }
-    
-    private static ASN1Sequence fromCertificate(
-        X509Certificate certificate)
-        throws CertificateParsingException
-    {
-        try
-        {
-            if (certificate.getVersion() != 3)
-            {
-                GeneralName          genName = new GeneralName(PrincipalUtil.getIssuerX509Principal(certificate));
-                SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
-                        (ASN1Sequence)new ASN1InputStream(certificate.getPublicKey().getEncoded()).readObject());
-                
-                return (ASN1Sequence)new AuthorityKeyIdentifier(
-                               info, new GeneralNames(genName), certificate.getSerialNumber()).toASN1Object();
-            }
-            else
-            {
-                GeneralName             genName = new GeneralName(PrincipalUtil.getIssuerX509Principal(certificate));
-                
-                byte[]                  ext = certificate.getExtensionValue(X509Extensions.SubjectKeyIdentifier.getId());
-                
-                if (ext != null)
-                {
-                    ASN1OctetString     str = (ASN1OctetString)X509ExtensionUtil.fromExtensionValue(ext);
-                
-                    return (ASN1Sequence)new AuthorityKeyIdentifier(
-                                    str.getOctets(), new GeneralNames(genName), certificate.getSerialNumber()).toASN1Object();
-                }
-                else
-                {
-                    SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
-                            (ASN1Sequence)new ASN1InputStream(certificate.getPublicKey().getEncoded()).readObject());
-                    
-                    return (ASN1Sequence)new AuthorityKeyIdentifier(
-                            info, new GeneralNames(genName), certificate.getSerialNumber()).toASN1Object();
-                }
-            }
-        }
-        catch (Exception e)
-        {
-            throw new CertificateParsingException("Exception extracting certificate details: " + e.toString());
-        }
-    }
-    
-    private static ASN1Sequence fromKey(
-        PublicKey pubKey)
-        throws InvalidKeyException
-    {
-        try
-        {
-            SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
-                                        (ASN1Sequence)new ASN1InputStream(pubKey.getEncoded()).readObject());
-        
-            return (ASN1Sequence)new AuthorityKeyIdentifier(info).toASN1Object();
-        }
-        catch (Exception e)
-        {
-            throw new InvalidKeyException("can't process key: " + e);
-        }
-    }
-    
-    /**
-     * Create an AuthorityKeyIdentifier using the passed in certificate's public
-     * key, issuer and serial number.
-     * 
-     * @param certificate the certificate providing the information.
-     * @throws CertificateParsingException if there is a problem processing the certificate
-     */
-    public AuthorityKeyIdentifierStructure(
-        X509Certificate certificate)
-        throws CertificateParsingException
-    {
-        super(fromCertificate(certificate));
-    }
-    
-    /**
-     * Create an AuthorityKeyIdentifier using just the hash of the 
-     * public key.
-     * 
-     * @param pubKey the key to generate the hash from.
-     * @throws InvalidKeyException if there is a problem using the key.
-     */
-    public AuthorityKeyIdentifierStructure(
-        PublicKey pubKey) 
-        throws InvalidKeyException
-    {
-        super(fromKey(pubKey));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/extension/SubjectKeyIdentifierStructure.java b/src/main/java/org/bouncycastle/x509/extension/SubjectKeyIdentifierStructure.java
deleted file mode 100644
index 0b7ecd6..0000000
--- a/src/main/java/org/bouncycastle/x509/extension/SubjectKeyIdentifierStructure.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.bouncycastle.x509.extension;
-
-import java.io.IOException;
-import java.security.PublicKey;
-import java.security.cert.CertificateParsingException;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-
-/**
- * A high level subject key identifier.
- */
-public class SubjectKeyIdentifierStructure
-    extends SubjectKeyIdentifier
-{
-    private AuthorityKeyIdentifier authKeyID;
-    
-    /**
-     * Constructor which will take the byte[] returned from getExtensionValue()
-     * 
-     * @param encodedValue a DER octet encoded string with the extension structure in it.
-     * @throws IOException on parsing errors.
-     */
-    public SubjectKeyIdentifierStructure(
-        byte[]  encodedValue)
-        throws IOException
-    {
-        super((ASN1OctetString)X509ExtensionUtil.fromExtensionValue(encodedValue));
-    }
-    
-    private static ASN1OctetString fromPublicKey(
-        PublicKey pubKey)
-        throws CertificateParsingException
-    {
-        try
-        {
-            SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
-                (ASN1Sequence)new ASN1InputStream(pubKey.getEncoded()).readObject());
-
-            return (ASN1OctetString)(new SubjectKeyIdentifier(info).toASN1Object());
-        }
-        catch (Exception e)
-        {
-            throw new CertificateParsingException("Exception extracting certificate details: " + e.toString());
-        }
-    }
-    
-    public SubjectKeyIdentifierStructure(
-        PublicKey pubKey)
-        throws CertificateParsingException
-    {
-        super(fromPublicKey(pubKey));
-    }
-}
diff --git a/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java b/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
index 22b1c12..b0d6e36 100644
--- a/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
+++ b/src/main/java/org/bouncycastle/x509/extension/X509ExtensionUtil.java
@@ -1,22 +1,102 @@
 package org.bouncycastle.x509.extension;
 
-import java.io.IOException;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERString;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.X509Name;
+
+import java.io.IOException;
+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;
 
 
 public class X509ExtensionUtil
 {
-    public static ASN1Encodable fromExtensionValue(
+    public static ASN1Object fromExtensionValue(
         byte[]  encodedValue) 
         throws IOException
     {
-        ASN1InputStream aIn = new ASN1InputStream(encodedValue);
+        ASN1OctetString octs = (ASN1OctetString)ASN1Object.fromByteArray(encodedValue);
         
-        aIn = new ASN1InputStream(((ASN1OctetString)aIn.readObject()).getOctets());
-        
-        return (ASN1Encodable)aIn.readObject();
+        return ASN1Object.fromByteArray(octs.getOctets());
+    }
+
+    public static Collection getIssuerAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {
+        byte[] extVal = cert.getExtensionValue(X509Extensions.IssuerAlternativeName.getId());
+
+        return getAlternativeNames(extVal);
+    }
+
+    public static Collection getSubjectAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {        
+        byte[] extVal = cert.getExtensionValue(X509Extensions.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(fromExtensionValue(extVal)).getObjects();
+            while (it.hasMoreElements())
+            {
+                GeneralName genName = GeneralName.getInstance(it.nextElement());
+                List list = new ArrayList();
+                // BEGIN android-changed
+                list.add(Integer.valueOf(genName.getTagNo()));
+                // END android-changed
+                switch (genName.getTagNo())
+                {
+                case GeneralName.ediPartyName:
+                case GeneralName.x400Address:
+                case GeneralName.otherName:
+                    list.add(genName.getName().getDERObject());
+                    break;
+                case GeneralName.directoryName:
+                    list.add(X509Name.getInstance(genName.getName()).toString());
+                    break;
+                case GeneralName.dNSName:
+                case GeneralName.rfc822Name:
+                case GeneralName.uniformResourceIdentifier:
+                    list.add(((DERString)genName.getName()).getString());
+                    break;
+                case GeneralName.registeredID:
+                    list.add(DERObjectIdentifier.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());
+        }
     }
 }