Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/asn1/x509')
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java98
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java148
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java91
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java84
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Attribute.java93
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java97
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java180
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java104
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java229
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java163
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java100
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java54
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java151
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java57
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Certificate.java131
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java144
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java168
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java124
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java92
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java86
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java165
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java158
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java138
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java192
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Extension.java321
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Extensions.java221
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java94
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java439
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java108
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java39
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java218
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Holder.java245
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java189
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java123
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java274
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java157
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java113
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java118
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java170
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java190
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java106
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java87
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java107
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java31
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java115
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java84
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java98
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java85
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java237
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java144
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java68
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java156
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java309
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java192
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java194
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Target.java138
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java120
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Targets.java121
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/Time.java169
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java132
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java144
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java158
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/V2Form.java157
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java281
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java212
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java29
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java129
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java65
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java249
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java477
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java94
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509Name.java1379
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java114
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java91
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java81
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java122
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java11
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java93
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java92
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java95
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java11
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java131
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java90
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java189
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java213
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java60
86 files changed, 13526 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java b/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java
new file mode 100644
index 00000000..29a57949
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AccessDescription.java
@@ -0,0 +1,98 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The AccessDescription object.
+ * <pre>
+ * AccessDescription ::= SEQUENCE {
+ * accessMethod OBJECT IDENTIFIER,
+ * accessLocation GeneralName }
+ * </pre>
+ */
+public class AccessDescription
+ extends ASN1Object
+{
+ public final static ASN1ObjectIdentifier id_ad_caIssuers = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.2");
+
+ public final static ASN1ObjectIdentifier id_ad_ocsp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1");
+
+ ASN1ObjectIdentifier accessMethod = null;
+ GeneralName accessLocation = null;
+
+ public static AccessDescription getInstance(
+ Object obj)
+ {
+ if (obj instanceof AccessDescription)
+ {
+ return (AccessDescription)obj;
+ }
+ else if (obj != null)
+ {
+ return new AccessDescription(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AccessDescription(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("wrong number of elements in sequence");
+ }
+
+ accessMethod = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ accessLocation = GeneralName.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * create an AccessDescription with the oid and location provided.
+ */
+ public AccessDescription(
+ ASN1ObjectIdentifier oid,
+ GeneralName location)
+ {
+ accessMethod = oid;
+ accessLocation = location;
+ }
+
+ /**
+ *
+ * @return the access method.
+ */
+ public ASN1ObjectIdentifier getAccessMethod()
+ {
+ return accessMethod;
+ }
+
+ /**
+ *
+ * @return the access location
+ */
+ public GeneralName getAccessLocation()
+ {
+ return accessLocation;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java b/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java
new file mode 100644
index 00000000..f5797250
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AlgorithmIdentifier.java
@@ -0,0 +1,148 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DERSequence;
+
+public class AlgorithmIdentifier
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier objectId;
+ private ASN1Encodable parameters;
+ private boolean parametersDefined = false;
+
+ public static AlgorithmIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AlgorithmIdentifier getInstance(
+ Object obj)
+ {
+ if (obj== null || obj instanceof AlgorithmIdentifier)
+ {
+ return (AlgorithmIdentifier)obj;
+ }
+
+ // TODO: delete
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ return new AlgorithmIdentifier((ASN1ObjectIdentifier)obj);
+ }
+
+ // TODO: delete
+ if (obj instanceof String)
+ {
+ return new AlgorithmIdentifier((String)obj);
+ }
+
+ return new AlgorithmIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ public AlgorithmIdentifier(
+ ASN1ObjectIdentifier objectId)
+ {
+ this.objectId = objectId;
+ }
+
+ /**
+ * @deprecated use ASN1ObjectIdentifier
+ * @param objectId
+ */
+ public AlgorithmIdentifier(
+ String objectId)
+ {
+ this.objectId = new ASN1ObjectIdentifier(objectId);
+ }
+
+ public AlgorithmIdentifier(
+ ASN1ObjectIdentifier objectId,
+ ASN1Encodable parameters)
+ {
+ parametersDefined = true;
+ this.objectId = objectId;
+ this.parameters = parameters;
+ }
+
+ /**
+ * @deprecated use AlgorithmIdentifier.getInstance()
+ * @param seq
+ */
+ public AlgorithmIdentifier(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ objectId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() == 2)
+ {
+ parametersDefined = true;
+ parameters = seq.getObjectAt(1);
+ }
+ else
+ {
+ parameters = null;
+ }
+ }
+
+ public ASN1ObjectIdentifier getAlgorithm()
+ {
+ return new ASN1ObjectIdentifier(objectId.getId());
+ }
+
+ /**
+ * @deprecated use getAlgorithm
+ * @return
+ */
+ public ASN1ObjectIdentifier getObjectId()
+ {
+ return objectId;
+ }
+
+ public ASN1Encodable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(objectId);
+
+ if (parametersDefined)
+ {
+ if (parameters != null)
+ {
+ v.add(parameters);
+ }
+ else
+ {
+ v.add(DERNull.INSTANCE);
+ }
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java b/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java
new file mode 100644
index 00000000..cb027fd1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AttCertIssuer.java
@@ -0,0 +1,91 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERTaggedObject;
+
+public class AttCertIssuer
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Encodable obj;
+ ASN1Primitive choiceObj;
+
+ public static AttCertIssuer getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof AttCertIssuer)
+ {
+ return (AttCertIssuer)obj;
+ }
+ else if (obj instanceof V2Form)
+ {
+ return new AttCertIssuer(V2Form.getInstance(obj));
+ }
+ else if (obj instanceof GeneralNames)
+ {
+ return new AttCertIssuer((GeneralNames)obj);
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new AttCertIssuer(V2Form.getInstance((ASN1TaggedObject)obj, false));
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new AttCertIssuer(GeneralNames.getInstance(obj));
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public static AttCertIssuer getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ /**
+ * 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
+ */
+ public AttCertIssuer(
+ GeneralNames names)
+ {
+ obj = names;
+ choiceObj = obj.toASN1Primitive();
+ }
+
+ public AttCertIssuer(
+ V2Form v2Form)
+ {
+ obj = v2Form;
+ choiceObj = new DERTaggedObject(false, 0, obj);
+ }
+
+ public ASN1Encodable getIssuer()
+ {
+ return obj;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttCertIssuer ::= CHOICE {
+ * v1Form GeneralNames, -- MUST NOT be used in this
+ * -- profile
+ * v2Form [0] V2Form -- v2 only
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return choiceObj;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java b/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java
new file mode 100644
index 00000000..eec4d634
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AttCertValidityPeriod.java
@@ -0,0 +1,84 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+public class AttCertValidityPeriod
+ extends ASN1Object
+{
+ ASN1GeneralizedTime notBeforeTime;
+ ASN1GeneralizedTime notAfterTime;
+
+ public static AttCertValidityPeriod getInstance(
+ Object obj)
+ {
+ if (obj instanceof AttCertValidityPeriod)
+ {
+ return (AttCertValidityPeriod)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttCertValidityPeriod(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AttCertValidityPeriod(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ notBeforeTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0));
+ notAfterTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * @param notBeforeTime
+ * @param notAfterTime
+ */
+ public AttCertValidityPeriod(
+ ASN1GeneralizedTime notBeforeTime,
+ ASN1GeneralizedTime notAfterTime)
+ {
+ this.notBeforeTime = notBeforeTime;
+ this.notAfterTime = notAfterTime;
+ }
+
+ public ASN1GeneralizedTime getNotBeforeTime()
+ {
+ return notBeforeTime;
+ }
+
+ public ASN1GeneralizedTime getNotAfterTime()
+ {
+ return notAfterTime;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttCertValidityPeriod ::= SEQUENCE {
+ * notBeforeTime GeneralizedTime,
+ * notAfterTime GeneralizedTime
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(notBeforeTime);
+ v.add(notAfterTime);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java b/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java
new file mode 100644
index 00000000..5a501e1f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Attribute.java
@@ -0,0 +1,93 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.DERSequence;
+
+public class Attribute
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier attrType;
+ private ASN1Set attrValues;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static Attribute getInstance(
+ Object o)
+ {
+ if (o instanceof Attribute)
+ {
+ return (Attribute)o;
+ }
+
+ if (o != null)
+ {
+ return new Attribute(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private Attribute(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ attrType = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ attrValues = ASN1Set.getInstance(seq.getObjectAt(1));
+ }
+
+ public Attribute(
+ ASN1ObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = attrType;
+ this.attrValues = attrValues;
+ }
+
+ public ASN1ObjectIdentifier getAttrType()
+ {
+ return new ASN1ObjectIdentifier(attrType.getId());
+ }
+
+ public ASN1Encodable[] getAttributeValues()
+ {
+ return attrValues.toArray();
+ }
+
+ public ASN1Set getAttrValues()
+ {
+ return attrValues;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ * attrType OBJECT IDENTIFIER,
+ * attrValues SET OF AttributeValue
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(attrType);
+ v.add(attrValues);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java b/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java
new file mode 100644
index 00000000..7af85919
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificate.java
@@ -0,0 +1,97 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+
+public class AttributeCertificate
+ extends ASN1Object
+{
+ AttributeCertificateInfo acinfo;
+ AlgorithmIdentifier signatureAlgorithm;
+ DERBitString signatureValue;
+
+ /**
+ * @param obj
+ * @return an AttributeCertificate object
+ */
+ public static AttributeCertificate getInstance(Object obj)
+ {
+ if (obj instanceof AttributeCertificate)
+ {
+ return (AttributeCertificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttributeCertificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public AttributeCertificate(
+ AttributeCertificateInfo acinfo,
+ AlgorithmIdentifier signatureAlgorithm,
+ DERBitString signatureValue)
+ {
+ this.acinfo = acinfo;
+ this.signatureAlgorithm = signatureAlgorithm;
+ this.signatureValue = signatureValue;
+ }
+
+ /**
+ * @deprecated use getInstance() method.
+ */
+ public AttributeCertificate(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0));
+ this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+
+ public AttributeCertificateInfo getAcinfo()
+ {
+ return acinfo;
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return signatureAlgorithm;
+ }
+
+ public DERBitString getSignatureValue()
+ {
+ return signatureValue;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * AttributeCertificate ::= SEQUENCE {
+ * acinfo AttributeCertificateInfo,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(acinfo);
+ v.add(signatureAlgorithm);
+ v.add(signatureValue);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java b/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java
new file mode 100644
index 00000000..33e3e86f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AttributeCertificateInfo.java
@@ -0,0 +1,180 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+
+public class AttributeCertificateInfo
+ extends ASN1Object
+{
+ private ASN1Integer version;
+ private Holder holder;
+ private AttCertIssuer issuer;
+ private AlgorithmIdentifier signature;
+ private ASN1Integer serialNumber;
+ private AttCertValidityPeriod attrCertValidityPeriod;
+ private ASN1Sequence attributes;
+ private DERBitString issuerUniqueID;
+ private Extensions extensions;
+
+ public static AttributeCertificateInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AttributeCertificateInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof AttributeCertificateInfo)
+ {
+ return (AttributeCertificateInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new AttributeCertificateInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AttributeCertificateInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 6 || seq.size() > 9)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int start;
+ if (seq.getObjectAt(0) instanceof ASN1Integer) // in version 1 certs version is DEFAULT v1(0)
+ {
+ this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+ start = 1;
+ }
+ else
+ {
+ this.version = new ASN1Integer(0);
+ start = 0;
+ }
+
+ this.holder = Holder.getInstance(seq.getObjectAt(start));
+ this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(start + 1));
+ this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(start + 2));
+ this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(start + 3));
+ this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(start + 4));
+ this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(start + 5));
+
+ for (int i = start + 6; i < seq.size(); i++)
+ {
+ ASN1Encodable obj = seq.getObjectAt(i);
+
+ if (obj instanceof DERBitString)
+ {
+ this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i));
+ }
+ else if (obj instanceof ASN1Sequence || obj instanceof Extensions)
+ {
+ this.extensions = Extensions.getInstance(seq.getObjectAt(i));
+ }
+ }
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public Holder getHolder()
+ {
+ return holder;
+ }
+
+ public AttCertIssuer getIssuer()
+ {
+ return issuer;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AttCertValidityPeriod getAttrCertValidityPeriod()
+ {
+ return attrCertValidityPeriod;
+ }
+
+ public ASN1Sequence getAttributes()
+ {
+ return attributes;
+ }
+
+ public DERBitString getIssuerUniqueID()
+ {
+ return issuerUniqueID;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <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
+ * }
+ *
+ * AttCertVersion ::= INTEGER { v2(1) }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version.getValue().intValue() != 0)
+ {
+ v.add(version);
+ }
+ v.add(holder);
+ v.add(issuer);
+ v.add(signature);
+ v.add(serialNumber);
+ v.add(attrCertValidityPeriod);
+ v.add(attributes);
+
+ if (issuerUniqueID != null)
+ {
+ v.add(issuerUniqueID);
+ }
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java b/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java
new file mode 100644
index 00000000..2324f747
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AuthorityInformationAccess.java
@@ -0,0 +1,104 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.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 ASN1Object
+{
+ private AccessDescription[] descriptions;
+
+ public static AuthorityInformationAccess getInstance(
+ Object obj)
+ {
+ if (obj instanceof AuthorityInformationAccess)
+ {
+ return (AuthorityInformationAccess)obj;
+ }
+
+ if (obj != null)
+ {
+ return new AuthorityInformationAccess(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private AuthorityInformationAccess(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1)
+ {
+ throw new IllegalArgumentException("sequence may not be empty");
+ }
+
+ descriptions = new AccessDescription[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ descriptions[i] = AccessDescription.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ public AuthorityInformationAccess(
+ AccessDescription description)
+ {
+ this.descriptions = new AccessDescription[]{ description };
+ }
+
+ /**
+ * create an AuthorityInformationAccess with the oid and location provided.
+ */
+ public AuthorityInformationAccess(
+ ASN1ObjectIdentifier oid,
+ GeneralName location)
+ {
+ this(new AccessDescription(oid, location));
+ }
+
+ /**
+ *
+ * @return the access descriptions contained in this object.
+ */
+ public AccessDescription[] getAccessDescriptions()
+ {
+ return descriptions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java b/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java
new file mode 100644
index 00000000..5178e3b8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -0,0 +1,229 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+
+/**
+ * The AuthorityKeyIdentifier object.
+ * <pre>
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
+ *
+ * AuthorityKeyIdentifier ::= SEQUENCE {
+ * keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ * authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ * authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ *
+ * KeyIdentifier ::= OCTET STRING
+ * </pre>
+ *
+ */
+public class AuthorityKeyIdentifier
+ extends ASN1Object
+{
+ ASN1OctetString keyidentifier=null;
+ GeneralNames certissuer=null;
+ ASN1Integer certserno=null;
+
+ public static AuthorityKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AuthorityKeyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof AuthorityKeyIdentifier)
+ {
+ return (AuthorityKeyIdentifier)obj;
+ }
+ if (obj != null)
+ {
+ return new AuthorityKeyIdentifier(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static AuthorityKeyIdentifier fromExtensions(Extensions extensions)
+ {
+ return AuthorityKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.authorityKeyIdentifier));
+ }
+
+ protected AuthorityKeyIdentifier(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = DERTaggedObject.getInstance(e.nextElement());
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ this.keyidentifier = ASN1OctetString.getInstance(o, false);
+ break;
+ case 1:
+ this.certissuer = GeneralNames.getInstance(o, false);
+ break;
+ case 2:
+ this.certserno = ASN1Integer.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("illegal tag");
+ }
+ }
+ }
+
+ /**
+ *
+ * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+ * from SubjectPublicKeyInfo as defined in RFC2459.
+ *
+ * Example of making a AuthorityKeyIdentifier:
+ * <pre>
+ * SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
+ * publicKey.getEncoded()).readObject());
+ * AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
+ * </pre>
+ * @deprecated create the extension using org.spongycastle.cert.X509ExtensionUtils
+ **/
+ public AuthorityKeyIdentifier(
+ 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 = new DEROctetString(resBuf);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with the GeneralNames tag and
+ * the serial number provided as well.
+ * @deprecated create the extension using org.spongycastle.cert.X509ExtensionUtils
+ */
+ public AuthorityKeyIdentifier(
+ SubjectPublicKeyInfo spki,
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ 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 = new DEROctetString(resBuf);
+ this.certissuer = GeneralNames.getInstance(name.toASN1Primitive());
+ this.certserno = new ASN1Integer(serialNumber);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with the GeneralNames tag and
+ * the serial number provided.
+ */
+ public AuthorityKeyIdentifier(
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ this((byte[])null, name, serialNumber);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with a precomputed key identifier
+ */
+ public AuthorityKeyIdentifier(
+ byte[] keyIdentifier)
+ {
+ this(keyIdentifier, null, null);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with a precomputed key identifier
+ * and the GeneralNames tag and the serial number provided as well.
+ */
+ public AuthorityKeyIdentifier(
+ byte[] keyIdentifier,
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(keyIdentifier) : null;
+ this.certissuer = name;
+ this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null;
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ if (keyidentifier != null)
+ {
+ return keyidentifier.getOctets();
+ }
+
+ return null;
+ }
+
+ public GeneralNames getAuthorityCertIssuer()
+ {
+ return certissuer;
+ }
+
+ public BigInteger getAuthorityCertSerialNumber()
+ {
+ if (certserno != null)
+ {
+ return certserno.getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (keyidentifier != null)
+ {
+ v.add(new DERTaggedObject(false, 0, keyidentifier));
+ }
+
+ if (certissuer != null)
+ {
+ v.add(new DERTaggedObject(false, 1, certissuer));
+ }
+
+ if (certserno != null)
+ {
+ v.add(new DERTaggedObject(false, 2, certserno));
+ }
+
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")");
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java b/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java
new file mode 100644
index 00000000..32f81cc9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/BasicConstraints.java
@@ -0,0 +1,163 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class BasicConstraints
+ extends ASN1Object
+{
+ ASN1Boolean cA = ASN1Boolean.getInstance(false);
+ ASN1Integer pathLenConstraint = null;
+
+ public static BasicConstraints getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static BasicConstraints getInstance(
+ Object obj)
+ {
+ if (obj instanceof BasicConstraints)
+ {
+ return (BasicConstraints)obj;
+ }
+ if (obj instanceof X509Extension)
+ {
+ return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
+ }
+ if (obj != null)
+ {
+ return new BasicConstraints(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static BasicConstraints fromExtensions(Extensions extensions)
+ {
+ return BasicConstraints.getInstance(extensions.getExtensionParsedValue(Extension.basicConstraints));
+ }
+
+ private BasicConstraints(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 0)
+ {
+ this.cA = null;
+ this.pathLenConstraint = null;
+ }
+ else
+ {
+ if (seq.getObjectAt(0) instanceof ASN1Boolean)
+ {
+ this.cA = ASN1Boolean.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ this.cA = null;
+ this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+ if (seq.size() > 1)
+ {
+ if (this.cA != null)
+ {
+ this.pathLenConstraint = ASN1Integer.getInstance(seq.getObjectAt(1));
+ }
+ else
+ {
+ throw new IllegalArgumentException("wrong sequence in constructor");
+ }
+ }
+ }
+ }
+
+ public BasicConstraints(
+ boolean cA)
+ {
+ if (cA)
+ {
+ this.cA = ASN1Boolean.getInstance(true);
+ }
+ else
+ {
+ this.cA = null;
+ }
+ this.pathLenConstraint = null;
+ }
+
+ /**
+ * create a cA=true object for the given path length constraint.
+ *
+ * @param pathLenConstraint
+ */
+ public BasicConstraints(
+ int pathLenConstraint)
+ {
+ this.cA = ASN1Boolean.getInstance(true);
+ this.pathLenConstraint = new ASN1Integer(pathLenConstraint);
+ }
+
+ public boolean isCA()
+ {
+ return (cA != null) && cA.isTrue();
+ }
+
+ public BigInteger getPathLenConstraint()
+ {
+ if (pathLenConstraint != null)
+ {
+ return pathLenConstraint.getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * BasicConstraints := SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (cA != null)
+ {
+ v.add(cA);
+ }
+
+ if (pathLenConstraint != null) // yes some people actually do this when cA is false...
+ {
+ v.add(pathLenConstraint);
+ }
+
+ return new DERSequence(v);
+ }
+
+ public String toString()
+ {
+ if (pathLenConstraint == null)
+ {
+ if (cA == null)
+ {
+ return "BasicConstraints: isCa(false)";
+ }
+ return "BasicConstraints: isCa(" + this.isCA() + ")";
+ }
+ return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java b/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java
new file mode 100644
index 00000000..739e98b5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CRLDistPoint.java
@@ -0,0 +1,100 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class CRLDistPoint
+ extends ASN1Object
+{
+ ASN1Sequence seq = null;
+
+ public static CRLDistPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CRLDistPoint getInstance(
+ Object obj)
+ {
+ if (obj instanceof CRLDistPoint)
+ {
+ return (CRLDistPoint)obj;
+ }
+ else if (obj != null)
+ {
+ return new CRLDistPoint(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private CRLDistPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public CRLDistPoint(
+ DistributionPoint[] points)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != points.length; i++)
+ {
+ v.add(points[i]);
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ /**
+ * Return the distribution points making up the sequence.
+ *
+ * @return DistributionPoint[]
+ */
+ public DistributionPoint[] getDistributionPoints()
+ {
+ DistributionPoint[] dp = new DistributionPoint[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ dp[i] = DistributionPoint.getInstance(seq.getObjectAt(i));
+ }
+
+ return dp;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java b/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java
new file mode 100644
index 00000000..7699fde5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CRLNumber.java
@@ -0,0 +1,54 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+
+/**
+ * The CRLNumber object.
+ * <pre>
+ * CRLNumber::= INTEGER(0..MAX)
+ * </pre>
+ */
+public class CRLNumber
+ extends ASN1Object
+{
+ private BigInteger number;
+
+ public CRLNumber(
+ BigInteger number)
+ {
+ this.number = number;
+ }
+
+ public BigInteger getCRLNumber()
+ {
+ return number;
+ }
+
+ public String toString()
+ {
+ return "CRLNumber: " + getCRLNumber();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new ASN1Integer(number);
+ }
+
+ public static CRLNumber getInstance(Object o)
+ {
+ if (o instanceof CRLNumber)
+ {
+ return (CRLNumber)o;
+ }
+ else if (o != null)
+ {
+ return new CRLNumber(ASN1Integer.getInstance(o).getValue());
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java b/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java
new file mode 100644
index 00000000..c1071699
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CRLReason.java
@@ -0,0 +1,151 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.util.Integers;
+
+/**
+ * The CRLReason enumeration.
+ * <pre>
+ * CRLReason ::= ENUMERATED {
+ * unspecified (0),
+ * keyCompromise (1),
+ * cACompromise (2),
+ * affiliationChanged (3),
+ * superseded (4),
+ * cessationOfOperation (5),
+ * certificateHold (6),
+ * removeFromCRL (8),
+ * privilegeWithdrawn (9),
+ * aACompromise (10)
+ * }
+ * </pre>
+ */
+public class CRLReason
+ extends ASN1Object
+{
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int UNSPECIFIED = 0;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int KEY_COMPROMISE = 1;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CA_COMPROMISE = 2;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AFFILIATION_CHANGED = 3;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int SUPERSEDED = 4;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CESSATION_OF_OPERATION = 5;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CERTIFICATE_HOLD = 6;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int REMOVE_FROM_CRL = 8;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int PRIVILEGE_WITHDRAWN = 9;
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AA_COMPROMISE = 10;
+
+ public static final int unspecified = 0;
+ public static final int keyCompromise = 1;
+ public static final int cACompromise = 2;
+ public static final int affiliationChanged = 3;
+ 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"
+ };
+
+ private static final Hashtable table = new Hashtable();
+
+ private ASN1Enumerated value;
+
+ public static CRLReason getInstance(Object o)
+ {
+ if (o instanceof CRLReason)
+ {
+ return (CRLReason)o;
+ }
+ else if (o != null)
+ {
+ return lookup(ASN1Enumerated.getInstance(o).getValue().intValue());
+ }
+
+ return null;
+ }
+
+ private CRLReason(
+ int reason)
+ {
+ value = new ASN1Enumerated(reason);
+ }
+
+ public String toString()
+ {
+ String str;
+ int reason = getValue().intValue();
+ if (reason < 0 || reason > 10)
+ {
+ str = "invalid";
+ }
+ else
+ {
+ str = reasonString[reason];
+ }
+ return "CRLReason: " + str;
+ }
+
+ public BigInteger getValue()
+ {
+ return value.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return value;
+ }
+
+ public static CRLReason lookup(int value)
+ {
+ Integer idx = Integers.valueOf(value);
+
+ if (!table.containsKey(idx))
+ {
+ table.put(idx, new CRLReason(value));
+ }
+
+ return (CRLReason)table.get(idx);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java b/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java
new file mode 100644
index 00000000..142b83e7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CertPolicyId.java
@@ -0,0 +1,57 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+
+
+/**
+ * CertPolicyId, used in the CertificatePolicies and PolicyMappings
+ * X509V3 Extensions.
+ *
+ * <pre>
+ * CertPolicyId ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+/**
+ * CertPolicyId, used in the CertificatePolicies and PolicyMappings
+ * X509V3 Extensions.
+ *
+ * <pre>
+ * CertPolicyId ::= OBJECT IDENTIFIER
+ * </pre>
+ */
+public class CertPolicyId
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier id;
+
+ private CertPolicyId(ASN1ObjectIdentifier id)
+ {
+ this.id = id;
+ }
+
+ public static CertPolicyId getInstance(Object o)
+ {
+ if (o instanceof CertPolicyId)
+ {
+ return (CertPolicyId)o;
+ }
+ else if (o != null)
+ {
+ return new CertPolicyId(ASN1ObjectIdentifier.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public String getId()
+ {
+ return id.getId();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java b/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java
new file mode 100644
index 00000000..8d1b69f2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Certificate.java
@@ -0,0 +1,131 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ */
+public class Certificate
+ extends ASN1Object
+{
+ ASN1Sequence seq;
+ TBSCertificate tbsCert;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static Certificate getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Certificate getInstance(
+ Object obj)
+ {
+ if (obj instanceof Certificate)
+ {
+ return (Certificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new Certificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private Certificate(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ //
+ // correct x509 certficate
+ //
+ if (seq.size() == 3)
+ {
+ tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for a certificate");
+ }
+ }
+
+ public TBSCertificate getTBSCertificate()
+ {
+ return tbsCert;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return tbsCert.getVersion();
+ }
+
+ public int getVersionNumber()
+ {
+ return tbsCert.getVersionNumber();
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return tbsCert.getSerialNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCert.getIssuer();
+ }
+
+ public Time getStartDate()
+ {
+ return tbsCert.getStartDate();
+ }
+
+ public Time getEndDate()
+ {
+ return tbsCert.getEndDate();
+ }
+
+ public X500Name getSubject()
+ {
+ return tbsCert.getSubject();
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return tbsCert.getSubjectPublicKeyInfo();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java b/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java
new file mode 100644
index 00000000..5d068198
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CertificateList.java
@@ -0,0 +1,144 @@
+
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * PKIX RFC-2459
+ *
+ * The X.509 v2 CRL syntax is as follows. For signature calculation,
+ * the data that is to be signed is ASN.1 DER encoded.
+ *
+ * <pre>
+ * CertificateList ::= SEQUENCE {
+ * tbsCertList TBSCertList,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING }
+ * </pre>
+ */
+public class CertificateList
+ extends ASN1Object
+{
+ TBSCertList tbsCertList;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+ boolean isHashCodeSet = false;
+ int hashCodeValue;
+
+ public static CertificateList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CertificateList getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificateList)
+ {
+ return (CertificateList)obj;
+ }
+ else if (obj != null)
+ {
+ return new CertificateList(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * @deprecated use getInstance() method.
+ * @param seq
+ */
+ public CertificateList(
+ ASN1Sequence seq)
+ {
+ if (seq.size() == 3)
+ {
+ tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for CertificateList");
+ }
+ }
+
+ public TBSCertList getTBSCertList()
+ {
+ return tbsCertList;
+ }
+
+ public TBSCertList.CRLEntry[] getRevokedCertificates()
+ {
+ return tbsCertList.getRevokedCertificates();
+ }
+
+ public Enumeration getRevokedCertificateEnumeration()
+ {
+ return tbsCertList.getRevokedCertificateEnumeration();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public int getVersionNumber()
+ {
+ return tbsCertList.getVersionNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCertList.getIssuer();
+ }
+
+ public Time getThisUpdate()
+ {
+ return tbsCertList.getThisUpdate();
+ }
+
+ public Time getNextUpdate()
+ {
+ return tbsCertList.getNextUpdate();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCertList);
+ v.add(sigAlgId);
+ v.add(sig);
+
+ return new DERSequence(v);
+ }
+
+ public int hashCode()
+ {
+ if (!isHashCodeSet)
+ {
+ hashCodeValue = super.hashCode();
+ isHashCodeSet = true;
+ }
+
+ return hashCodeValue;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java b/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java
new file mode 100644
index 00000000..976473fd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CertificatePair.java
@@ -0,0 +1,168 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * This class helps to support crossCerfificatePairs in a LDAP directory
+ * according RFC 2587
+ *
+ * <pre>
+ * crossCertificatePairATTRIBUTE::={
+ * WITH SYNTAX CertificatePair
+ * EQUALITY MATCHING RULE certificatePairExactMatch
+ * ID joint-iso-ccitt(2) ds(5) attributeType(4) crossCertificatePair(40)}
+ * </pre>
+ *
+ * <blockquote> The forward elements of the crossCertificatePair attribute of a
+ * CA's directory entry shall be used to store all, except self-issued
+ * certificates issued to this CA. Optionally, the reverse elements of the
+ * crossCertificatePair attribute, of a CA's directory entry may contain a
+ * subset of certificates issued by this CA to other CAs. When both the forward
+ * and the reverse elements are present in a single attribute value, issuer name
+ * in one certificate shall match the subject name in the other and vice versa,
+ * and the subject public key in one certificate shall be capable of verifying
+ * the digital signature on the other certificate and vice versa.
+ *
+ * When a reverse element is present, the forward element value and the reverse
+ * element value need not be stored in the same attribute value; in other words,
+ * they can be stored in either a single attribute value or two attribute
+ * values. </blockquote>
+ *
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ */
+public class CertificatePair
+ extends ASN1Object
+{
+ private Certificate forward;
+
+ private Certificate reverse;
+
+ public static CertificatePair getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof CertificatePair)
+ {
+ return (CertificatePair)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new CertificatePair((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type CertificatePair:
+ * <p/>
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private CertificatePair(ASN1Sequence seq)
+ {
+ if (seq.size() != 1 && seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ if (o.getTagNo() == 0)
+ {
+ forward = Certificate.getInstance(o, true);
+ }
+ else if (o.getTagNo() == 1)
+ {
+ reverse = Certificate.getInstance(o, true);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param forward Certificates issued to this CA.
+ * @param reverse Certificates issued by this CA to other CAs.
+ */
+ public CertificatePair(Certificate forward, Certificate reverse)
+ {
+ this.forward = forward;
+ this.reverse = reverse;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p>
+ * Returns:
+ * <pre>
+ * CertificatePair ::= SEQUENCE {
+ * forward [0] Certificate OPTIONAL,
+ * reverse [1] Certificate OPTIONAL,
+ * -- at least one of the pair shall be present -- }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+
+ if (forward != null)
+ {
+ vec.add(new DERTaggedObject(0, forward));
+ }
+ if (reverse != null)
+ {
+ vec.add(new DERTaggedObject(1, reverse));
+ }
+
+ return new DERSequence(vec);
+ }
+
+ /**
+ * @return Returns the forward.
+ */
+ public Certificate getForward()
+ {
+ return forward;
+ }
+
+ /**
+ * @return Returns the reverse.
+ */
+ public Certificate getReverse()
+ {
+ return reverse;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java b/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java
new file mode 100644
index 00000000..d8710ae7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/CertificatePolicies.java
@@ -0,0 +1,124 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class CertificatePolicies
+ extends ASN1Object
+{
+ private final PolicyInformation[] policyInformation;
+
+ public static CertificatePolicies getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificatePolicies)
+ {
+ return (CertificatePolicies)obj;
+ }
+
+ if (obj != null)
+ {
+ return new CertificatePolicies(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static CertificatePolicies getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Retrieve a CertificatePolicies for a passed in Extensions object, if present.
+ *
+ * @param extensions the extensions object to be examined.
+ * @return the CertificatePolicies, null if the extension is not present.
+ */
+ public static CertificatePolicies fromExtensions(Extensions extensions)
+ {
+ return CertificatePolicies.getInstance(extensions.getExtensionParsedValue(Extension.certificatePolicies));
+ }
+
+ /**
+ * Construct a CertificatePolicies object containing one PolicyInformation.
+ *
+ * @param name the name to be contained.
+ */
+ public CertificatePolicies(
+ PolicyInformation name)
+ {
+ this.policyInformation = new PolicyInformation[] { name };
+ }
+
+ public CertificatePolicies(
+ PolicyInformation[] policyInformation)
+ {
+ this.policyInformation = policyInformation;
+ }
+
+ private CertificatePolicies(
+ ASN1Sequence seq)
+ {
+ this.policyInformation = new PolicyInformation[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ policyInformation[i] = PolicyInformation.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ public PolicyInformation[] getPolicyInformation()
+ {
+ PolicyInformation[] tmp = new PolicyInformation[policyInformation.length];
+
+ System.arraycopy(policyInformation, 0, tmp, 0, policyInformation.length);
+
+ return tmp;
+ }
+
+ public PolicyInformation getPolicyInformation(ASN1ObjectIdentifier policyIdentifier)
+ {
+ for (int i = 0; i != policyInformation.length; i++)
+ {
+ if (policyIdentifier.equals(policyInformation[i].getPolicyIdentifier()))
+ {
+ return policyInformation[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * CertificatePolicies ::= SEQUENCE SIZE {1..MAX} OF PolicyInformation
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(policyInformation);
+ }
+
+ public String toString()
+ {
+ String p = null;
+ for (int i = 0; i < policyInformation.length; i++)
+ {
+ if (p != null)
+ {
+ p += ", ";
+ }
+ p += policyInformation[i];
+ }
+
+ return "CertificatePolicies: " + p;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java b/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java
new file mode 100644
index 00000000..1b6b2405
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/DSAParameter.java
@@ -0,0 +1,92 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class DSAParameter
+ extends ASN1Object
+{
+ ASN1Integer p, q, g;
+
+ public static DSAParameter getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DSAParameter getInstance(
+ Object obj)
+ {
+ if (obj instanceof DSAParameter)
+ {
+ return (DSAParameter)obj;
+ }
+
+ if(obj != null)
+ {
+ return new DSAParameter(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DSAParameter(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ {
+ this.p = new ASN1Integer(p);
+ this.q = new ASN1Integer(q);
+ this.g = new ASN1Integer(g);
+ }
+
+ private DSAParameter(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ p = ASN1Integer.getInstance(e.nextElement());
+ q = ASN1Integer.getInstance(e.nextElement());
+ g = ASN1Integer.getInstance(e.nextElement());
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getQ()
+ {
+ return q.getPositiveValue();
+ }
+
+ public BigInteger getG()
+ {
+ return g.getPositiveValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(p);
+ v.add(q);
+ v.add(g);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java b/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java
new file mode 100644
index 00000000..b5f5cf61
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/DigestInfo.java
@@ -0,0 +1,86 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The DigestInfo object.
+ * <pre>
+ * DigestInfo::=SEQUENCE{
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ * </pre>
+ */
+public class DigestInfo
+ extends ASN1Object
+{
+ private byte[] digest;
+ private AlgorithmIdentifier algId;
+
+ public static DigestInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DigestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof DigestInfo)
+ {
+ return (DigestInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new DigestInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public DigestInfo(
+ AlgorithmIdentifier algId,
+ byte[] digest)
+ {
+ this.digest = digest;
+ this.algId = algId;
+ }
+
+ public DigestInfo(
+ ASN1Sequence obj)
+ {
+ Enumeration e = obj.getObjects();
+
+ algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ digest = ASN1OctetString.getInstance(e.nextElement()).getOctets();
+ }
+
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(new DEROctetString(digest));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java b/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java
new file mode 100644
index 00000000..ad273cf8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/DisplayText.java
@@ -0,0 +1,165 @@
+
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBMPString;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.DERVisibleString;
+
+/**
+ * <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 ASN1Object
+ 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;
+ ASN1String 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 = new DERIA5String(text);
+ break;
+ case CONTENT_TYPE_UTF8STRING:
+ contents = new DERUTF8String(text);
+ break;
+ case CONTENT_TYPE_VISIBLESTRING:
+ contents = new DERVisibleString(text);
+ break;
+ case CONTENT_TYPE_BMPSTRING:
+ contents = new DERBMPString(text);
+ break;
+ default:
+ contents = 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.
+ */
+ private DisplayText(ASN1String de)
+ {
+ contents = de;
+ }
+
+ public static DisplayText getInstance(Object obj)
+ {
+ if (obj instanceof ASN1String)
+ {
+ return new DisplayText((ASN1String)obj);
+ }
+ else if (obj == null || obj instanceof DisplayText)
+ {
+ return (DisplayText)obj;
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static DisplayText getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return (ASN1Primitive)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/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java b/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java
new file mode 100644
index 00000000..d854e854
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/DistributionPoint.java
@@ -0,0 +1,158 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * The DistributionPoint object.
+ * <pre>
+ * DistributionPoint ::= SEQUENCE {
+ * distributionPoint [0] DistributionPointName OPTIONAL,
+ * reasons [1] ReasonFlags OPTIONAL,
+ * cRLIssuer [2] GeneralNames OPTIONAL
+ * }
+ * </pre>
+ */
+public class DistributionPoint
+ extends ASN1Object
+{
+ DistributionPointName distributionPoint;
+ ReasonFlags reasons;
+ GeneralNames cRLIssuer;
+
+ public static DistributionPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DistributionPoint getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof DistributionPoint)
+ {
+ return (DistributionPoint)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new DistributionPoint((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DistributionPoint: " + obj.getClass().getName());
+ }
+
+ public DistributionPoint(
+ ASN1Sequence seq)
+ {
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject t = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ switch (t.getTagNo())
+ {
+ case 0:
+ distributionPoint = DistributionPointName.getInstance(t, true);
+ break;
+ case 1:
+ reasons = new ReasonFlags(DERBitString.getInstance(t, false));
+ break;
+ case 2:
+ cRLIssuer = GeneralNames.getInstance(t, false);
+ }
+ }
+ }
+
+ public DistributionPoint(
+ DistributionPointName distributionPoint,
+ ReasonFlags reasons,
+ GeneralNames cRLIssuer)
+ {
+ this.distributionPoint = distributionPoint;
+ this.reasons = reasons;
+ this.cRLIssuer = cRLIssuer;
+ }
+
+ public DistributionPointName getDistributionPoint()
+ {
+ return distributionPoint;
+ }
+
+ public ReasonFlags getReasons()
+ {
+ return reasons;
+ }
+
+ public GeneralNames getCRLIssuer()
+ {
+ return cRLIssuer;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (distributionPoint != null)
+ {
+ //
+ // as this is a CHOICE it must be explicitly tagged
+ //
+ v.add(new DERTaggedObject(0, distributionPoint));
+ }
+
+ if (reasons != null)
+ {
+ v.add(new DERTaggedObject(false, 1, reasons));
+ }
+
+ if (cRLIssuer != null)
+ {
+ v.add(new DERTaggedObject(false, 2, cRLIssuer));
+ }
+
+ 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/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java b/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java
new file mode 100644
index 00000000..f0033fb8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/DistributionPointName.java
@@ -0,0 +1,138 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * The DistributionPointName object.
+ * <pre>
+ * DistributionPointName ::= CHOICE {
+ * fullName [0] GeneralNames,
+ * nameRelativeToCRLIssuer [1] RDN
+ * }
+ * </pre>
+ */
+public class DistributionPointName
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Encodable name;
+ int type;
+
+ public static final int FULL_NAME = 0;
+ public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1;
+
+ public static DistributionPointName getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1TaggedObject.getInstance(obj, true));
+ }
+
+ public static DistributionPointName getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DistributionPointName)
+ {
+ return (DistributionPointName)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new DistributionPointName((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public DistributionPointName(
+ int type,
+ ASN1Encodable name)
+ {
+ this.type = type;
+ this.name = name;
+ }
+
+ public DistributionPointName(
+ GeneralNames name)
+ {
+ this(FULL_NAME, name);
+ }
+
+ /**
+ * Return the tag number applying to the underlying choice.
+ *
+ * @return the tag number for this point name.
+ */
+ public int getType()
+ {
+ return this.type;
+ }
+
+ /**
+ * Return the tagged object inside the distribution point name.
+ *
+ * @return the underlying choice item.
+ */
+ public ASN1Encodable getName()
+ {
+ return (ASN1Encodable)name;
+ }
+
+ public DistributionPointName(
+ ASN1TaggedObject obj)
+ {
+ this.type = obj.getTagNo();
+
+ if (type == 0)
+ {
+ this.name = GeneralNames.getInstance(obj, false);
+ }
+ else
+ {
+ this.name = ASN1Set.getInstance(obj, false);
+ }
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java b/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java
new file mode 100644
index 00000000..c430210f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/ExtendedKeyUsage.java
@@ -0,0 +1,192 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The extendedKeyUsage object.
+ * <pre>
+ * extendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ * </pre>
+ */
+public class ExtendedKeyUsage
+ extends ASN1Object
+{
+ Hashtable usageTable = new Hashtable();
+ ASN1Sequence seq;
+
+ /**
+ * Return an ExtendedKeyUsage from the passed in tagged object.
+ *
+ * @param obj the tagged object containing the ExtendedKeyUsage
+ * @param explicit true if the tagged object should be interpreted as explicitly tagged, false if implicit.
+ * @return the ExtendedKeyUsage contained.
+ */
+ public static ExtendedKeyUsage getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * Return an ExtendedKeyUsage from the passed in object.
+ *
+ * @param obj an ExtendedKeyUsage, some form or encoding of one, or null.
+ * @return an ExtendedKeyUsage object, or null if null is passed in.
+ */
+ public static ExtendedKeyUsage getInstance(
+ Object obj)
+ {
+ if (obj instanceof ExtendedKeyUsage)
+ {
+ return (ExtendedKeyUsage)obj;
+ }
+ else if (obj != null)
+ {
+ return new ExtendedKeyUsage(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve an ExtendedKeyUsage for a passed in Extensions object, if present.
+ *
+ * @param extensions the extensions object to be examined.
+ * @return the ExtendedKeyUsage, null if the extension is not present.
+ */
+ public static ExtendedKeyUsage fromExtensions(Extensions extensions)
+ {
+ return ExtendedKeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.extendedKeyUsage));
+ }
+
+ /**
+ * Base constructor, from a single KeyPurposeId.
+ *
+ * @param usage the keyPurposeId to be included.
+ */
+ public ExtendedKeyUsage(
+ KeyPurposeId usage)
+ {
+ this.seq = new DERSequence(usage);
+
+ this.usageTable.put(usage, usage);
+ }
+
+ private ExtendedKeyUsage(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable o = (ASN1Encodable)e.nextElement();
+ if (!(o.toASN1Primitive() instanceof ASN1ObjectIdentifier))
+ {
+ throw new IllegalArgumentException("Only ASN1ObjectIdentifiers allowed in ExtendedKeyUsage.");
+ }
+ this.usageTable.put(o, o);
+ }
+ }
+
+ /**
+ * Base constructor, from multiple KeyPurposeIds.
+ *
+ * @param usages an array of KeyPurposeIds.
+ */
+ public ExtendedKeyUsage(
+ KeyPurposeId[] usages)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != usages.length; i++)
+ {
+ v.add(usages[i]);
+ this.usageTable.put(usages[i], usages[i]);
+ }
+
+ this.seq = new DERSequence(v);
+ }
+
+ /**
+ * @deprecated use KeyPurposeId[] constructor.
+ */
+ public ExtendedKeyUsage(
+ Vector usages)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ Enumeration e = usages.elements();
+
+ while (e.hasMoreElements())
+ {
+ KeyPurposeId o = KeyPurposeId.getInstance(e.nextElement());
+
+ v.add(o);
+ this.usageTable.put(o, o);
+ }
+
+ this.seq = new DERSequence(v);
+ }
+
+ /**
+ * Return true if this ExtendedKeyUsage object contains the passed in keyPurposeId.
+ *
+ * @param keyPurposeId the KeyPurposeId of interest.
+ * @return true if the keyPurposeId is present, false otherwise.
+ */
+ public boolean hasKeyPurposeId(
+ KeyPurposeId keyPurposeId)
+ {
+ return (usageTable.get(keyPurposeId) != null);
+ }
+
+ /**
+ * Returns all extended key usages.
+ *
+ * @return An array with all key purposes.
+ */
+ public KeyPurposeId[] getUsages()
+ {
+ KeyPurposeId[] temp = new KeyPurposeId[seq.size()];
+
+ int i = 0;
+ for (Enumeration it = seq.getObjects(); it.hasMoreElements();)
+ {
+ temp[i++] = KeyPurposeId.getInstance(it.nextElement());
+ }
+ return temp;
+ }
+
+ /**
+ * Return the number of KeyPurposeIds present in this ExtendedKeyUsage.
+ *
+ * @return the number of KeyPurposeIds
+ */
+ public int size()
+ {
+ return usageTable.size();
+ }
+
+ /**
+ * Return the ASN.1 primitive form of this object.
+ *
+ * @return an ASN1Sequence.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Extension.java b/core/src/main/java/org/spongycastle/asn1/x509/Extension.java
new file mode 100644
index 00000000..b34f191e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Extension.java
@@ -0,0 +1,321 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ */
+public class Extension
+ extends ASN1Object
+{
+ /**
+ * Subject Directory Attributes
+ */
+ public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ */
+ public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ */
+ public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ */
+ public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ */
+ public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ */
+ public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ */
+ public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ */
+ public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ */
+ public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ */
+ public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ */
+ public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ */
+ public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ */
+ public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ */
+ public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ */
+ public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ */
+ public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ */
+ public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ */
+ public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ */
+ public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ */
+ public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ */
+ public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ */
+ public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ */
+ public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ */
+ public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ */
+ public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ */
+ public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ */
+ public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ */
+ public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ private ASN1ObjectIdentifier extnId;
+ private boolean critical;
+ private ASN1OctetString value;
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ ASN1Boolean critical,
+ ASN1OctetString value)
+ {
+ this(extnId, critical.isTrue(), value);
+ }
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ boolean critical,
+ byte[] value)
+ {
+ this(extnId, critical, new DEROctetString(value));
+ }
+
+ public Extension(
+ ASN1ObjectIdentifier extnId,
+ boolean critical,
+ ASN1OctetString value)
+ {
+ this.extnId = extnId;
+ this.critical = critical;
+ this.value = value;
+ }
+
+ private Extension(ASN1Sequence seq)
+ {
+ if (seq.size() == 2)
+ {
+ this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ this.critical = false;
+ this.value = ASN1OctetString.getInstance(seq.getObjectAt(1));
+ }
+ else if (seq.size() == 3)
+ {
+ this.extnId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ this.critical = ASN1Boolean.getInstance(seq.getObjectAt(1)).isTrue();
+ this.value = ASN1OctetString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+ }
+
+ public static Extension getInstance(Object obj)
+ {
+ if (obj instanceof Extension)
+ {
+ return (Extension)obj;
+ }
+ else if (obj != null)
+ {
+ return new Extension(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public ASN1ObjectIdentifier getExtnId()
+ {
+ return extnId;
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public ASN1OctetString getExtnValue()
+ {
+ return value;
+ }
+
+ public ASN1Encodable getParsedValue()
+ {
+ return convertValueToObject(this);
+ }
+
+ public int hashCode()
+ {
+ if (this.isCritical())
+ {
+ return this.getExtnValue().hashCode() ^ this.getExtnId().hashCode();
+ }
+
+ return ~(this.getExtnValue().hashCode() ^ this.getExtnId().hashCode());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof Extension))
+ {
+ return false;
+ }
+
+ Extension other = (Extension)o;
+
+ return other.getExtnId().equals(this.getExtnId())
+ && other.getExtnValue().equals(this.getExtnValue())
+ && (other.isCritical() == this.isCritical());
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(extnId);
+
+ if (critical)
+ {
+ v.add(ASN1Boolean.getInstance(true));
+ }
+
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+
+ /**
+ * 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
+ */
+ private static ASN1Primitive convertValueToObject(
+ Extension ext)
+ throws IllegalArgumentException
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(ext.getExtnValue().getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't convert extension: " + e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java b/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java
new file mode 100644
index 00000000..9678240a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Extensions.java
@@ -0,0 +1,221 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class Extensions
+ extends ASN1Object
+{
+ private Hashtable extensions = new Hashtable();
+ private Vector ordering = new Vector();
+
+ public static Extensions getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static Extensions getInstance(
+ Object obj)
+ {
+ if (obj instanceof Extensions)
+ {
+ return (Extensions)obj;
+ }
+ else if (obj != null)
+ {
+ return new Extensions(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+ */
+ private Extensions(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Extension ext = Extension.getInstance(e.nextElement());
+
+ extensions.put(ext.getExtnId(), ext);
+ ordering.addElement(ext.getExtnId());
+ }
+ }
+
+ /**
+ * Base Constructor
+ *
+ * @param extension a single extension.
+ */
+ public Extensions(
+ Extension extension)
+ {
+ this.ordering.addElement(extension.getExtnId());
+ this.extensions.put(extension.getExtnId(), extension);
+ }
+
+ /**
+ * Base Constructor
+ *
+ * @param extensions an array of extensions.
+ */
+ public Extensions(
+ Extension[] extensions)
+ {
+ for (int i = 0; i != extensions.length; i++)
+ {
+ Extension ext = extensions[i];
+
+ this.ordering.addElement(ext.getExtnId());
+ this.extensions.put(ext.getExtnId(), ext);
+ }
+ }
+
+ /**
+ * return an Enumeration of the extension field's object ids.
+ */
+ public Enumeration oids()
+ {
+ return ordering.elements();
+ }
+
+ /**
+ * return the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the extension if it's present, null otherwise.
+ */
+ public Extension getExtension(
+ ASN1ObjectIdentifier oid)
+ {
+ return (Extension)extensions.get(oid);
+ }
+
+ /**
+ * return the parsed value of the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the parsed value of the extension if it's present, null otherwise.
+ */
+ public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid)
+ {
+ Extension ext = this.getExtension(oid);
+
+ if (ext != null)
+ {
+ return ext.getParsedValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * <pre>
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnId EXTENSION.&amp;id ({ExtensionSet}),
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ Enumeration e = ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = (Extension)extensions.get(oid);
+
+ vec.add(ext);
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public boolean equivalent(
+ Extensions other)
+ {
+ if (extensions.size() != other.extensions.size())
+ {
+ return false;
+ }
+
+ Enumeration e1 = extensions.keys();
+
+ while (e1.hasMoreElements())
+ {
+ Object key = e1.nextElement();
+
+ if (!extensions.get(key).equals(other.extensions.get(key)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public ASN1ObjectIdentifier[] getExtensionOIDs()
+ {
+ return toOidArray(ordering);
+ }
+
+ public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public ASN1ObjectIdentifier[] getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical)
+ {
+ Vector oidVec = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ Object oid = ordering.elementAt(i);
+
+ if (((Extension)extensions.get(oid)).isCritical() == isCritical)
+ {
+ oidVec.addElement(oid);
+ }
+ }
+
+ return toOidArray(oidVec);
+ }
+
+ private ASN1ObjectIdentifier[] toOidArray(Vector oidVec)
+ {
+ ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i);
+ }
+ return oids;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java
new file mode 100644
index 00000000..1f2f571d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/ExtensionsGenerator.java
@@ -0,0 +1,94 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DEROctetString;
+
+/**
+ * Generator for X.509 extensions
+ */
+public class ExtensionsGenerator
+{
+ 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(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ throws IOException
+ {
+ this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+
+ /**
+ * 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(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ byte[] value)
+ {
+ if (extensions.containsKey(oid))
+ {
+ throw new IllegalArgumentException("extension " + oid + " already added");
+ }
+
+ extOrdering.addElement(oid);
+ extensions.put(oid, new Extension(oid, 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 Extensions object based on the current state of the generator.
+ *
+ * @return an X09Extensions object.
+ */
+ public Extensions generate()
+ {
+ Extension[] exts = new Extension[extOrdering.size()];
+
+ for (int i = 0; i != extOrdering.size(); i++)
+ {
+ exts[i] = (Extension)extensions.get(extOrdering.elementAt(i));
+ }
+
+ return new Extensions(exts);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java b/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java
new file mode 100644
index 00000000..86ad11e5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java
@@ -0,0 +1,439 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.util.IPAddress;
+
+/**
+ * The GeneralName object.
+ * <pre>
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString OPTIONAL,
+ * partyName [1] DirectoryString }
+ *
+ * Name ::= CHOICE { RDNSequence }
+ * </pre>
+ */
+public class GeneralName
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int otherName = 0;
+ public static final int rfc822Name = 1;
+ public static final int dNSName = 2;
+ public static final int x400Address = 3;
+ public static final int directoryName = 4;
+ public static final int ediPartyName = 5;
+ public static final int uniformResourceIdentifier = 6;
+ public static final int iPAddress = 7;
+ public static final int registeredID = 8;
+
+ private ASN1Encodable obj;
+ private int tag;
+
+ /**
+ * @deprecated use X500Name constructor.
+ * @param dirName
+ */
+ public GeneralName(
+ X509Name dirName)
+ {
+ this.obj = X500Name.getInstance(dirName);
+ this.tag = 4;
+ }
+
+ public GeneralName(
+ X500Name dirName)
+ {
+ this.obj = dirName;
+ this.tag = 4;
+ }
+
+ /**
+ * When the subjectAltName extension contains an Internet mail address,
+ * the address MUST be included as an rfc822Name. The format of an
+ * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822].
+ *
+ * When the subjectAltName extension contains a domain name service
+ * label, the domain name MUST be stored in the dNSName (an IA5String).
+ * The name MUST be in the "preferred name syntax," as specified by RFC
+ * 1034 [RFC 1034].
+ *
+ * When the subjectAltName extension contains a URI, the name MUST be
+ * stored in the uniformResourceIdentifier (an IA5String). The name MUST
+ * be a non-relative URL, and MUST follow the URL syntax and encoding
+ * rules specified in [RFC 1738]. The name must include both a scheme
+ * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme-
+ * specific-part must include a fully qualified domain name or IP
+ * address as the host.
+ *
+ * When the subjectAltName extension contains a iPAddress, the address
+ * MUST be stored in the octet string in "network byte order," as
+ * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of
+ * each octet is the LSB of the corresponding byte in the network
+ * address. For IP Version 4, as specified in RFC 791, the octet string
+ * MUST contain exactly four octets. For IP Version 6, as specified in
+ * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC
+ * 1883].
+ */
+ public GeneralName(
+ int tag,
+ ASN1Encodable name)
+ {
+ this.obj = name;
+ this.tag = tag;
+ }
+
+ /**
+ * 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.obj = new DERIA5String(name);
+ }
+ else if (tag == registeredID)
+ {
+ this.obj = new ASN1ObjectIdentifier(name);
+ }
+ else if (tag == directoryName)
+ {
+ this.obj = new X500Name(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);
+ }
+ }
+
+ public static GeneralName getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof GeneralName)
+ {
+ return (GeneralName)obj;
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ ASN1TaggedObject tagObj = (ASN1TaggedObject)obj;
+ int tag = tagObj.getTagNo();
+
+ switch (tag)
+ {
+ case otherName:
+ return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
+ case rfc822Name:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case dNSName:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case x400Address:
+ throw new IllegalArgumentException("unknown tag: " + tag);
+ case directoryName:
+ return new GeneralName(tag, X500Name.getInstance(tagObj, true));
+ case ediPartyName:
+ return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
+ case uniformResourceIdentifier:
+ return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+ case iPAddress:
+ return new GeneralName(tag, ASN1OctetString.getInstance(tagObj, false));
+ case registeredID:
+ return new GeneralName(tag, ASN1ObjectIdentifier.getInstance(tagObj, false));
+ }
+ }
+
+ if (obj instanceof byte[])
+ {
+ try
+ {
+ return getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to parse encoded general name");
+ }
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static GeneralName getInstance(
+ ASN1TaggedObject tagObj,
+ boolean explicit)
+ {
+ return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
+ }
+
+ public int getTagNo()
+ {
+ return tag;
+ }
+
+ public ASN1Encodable getName()
+ {
+ 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(X500Name.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 << (7 - (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 << (15 - (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 ASN1Primitive toASN1Primitive()
+ {
+ if (tag == directoryName) // directoryName is explicitly tagged as it is a CHOICE
+ {
+ return new DERTaggedObject(true, tag, obj);
+ }
+ else
+ {
+ return new DERTaggedObject(false, tag, obj);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java b/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java
new file mode 100644
index 00000000..bf8c21fa
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/GeneralNames.java
@@ -0,0 +1,108 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+public class GeneralNames
+ extends ASN1Object
+{
+ private final GeneralName[] names;
+
+ public static GeneralNames getInstance(
+ Object obj)
+ {
+ if (obj instanceof GeneralNames)
+ {
+ return (GeneralNames)obj;
+ }
+
+ if (obj != null)
+ {
+ return new GeneralNames(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static GeneralNames getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static GeneralNames fromExtensions(Extensions extensions, ASN1ObjectIdentifier extOID)
+ {
+ return GeneralNames.getInstance(extensions.getExtensionParsedValue(extOID));
+ }
+
+ /**
+ * Construct a GeneralNames object containing one GeneralName.
+ *
+ * @param name the name to be contained.
+ */
+ public GeneralNames(
+ GeneralName name)
+ {
+ this.names = new GeneralName[] { name };
+ }
+
+
+ public GeneralNames(
+ GeneralName[] names)
+ {
+ this.names = names;
+ }
+
+ private GeneralNames(
+ ASN1Sequence seq)
+ {
+ this.names = new GeneralName[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ names[i] = GeneralName.getInstance(seq.getObjectAt(i));
+ }
+ }
+
+ public GeneralName[] getNames()
+ {
+ GeneralName[] tmp = new GeneralName[names.length];
+
+ System.arraycopy(names, 0, tmp, 0, names.length);
+
+ return tmp;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java b/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java
new file mode 100644
index 00000000..2d4b94a8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/GeneralNamesBuilder.java
@@ -0,0 +1,39 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Vector;
+
+public class GeneralNamesBuilder
+{
+ private Vector names = new Vector();
+
+ public GeneralNamesBuilder addNames(GeneralNames names)
+ {
+ GeneralName[] n = names.getNames();
+
+ for (int i = 0; i != n.length; i++)
+ {
+ this.names.addElement(n[i]);
+ }
+
+ return this;
+ }
+
+ public GeneralNamesBuilder addName(GeneralName name)
+ {
+ names.addElement(name);
+
+ return this;
+ }
+
+ public GeneralNames build()
+ {
+ GeneralName[] tmp = new GeneralName[names.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (GeneralName)names.elementAt(i);
+ }
+
+ return new GeneralNames(tmp);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java b/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java
new file mode 100644
index 00000000..82084e55
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/GeneralSubtree.java
@@ -0,0 +1,218 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * Class for containing a restriction object subtrees in NameConstraints. See
+ * RFC 3280.
+ *
+ * <pre>
+ *
+ * GeneralSubtree ::= SEQUENCE
+ * {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.spongycastle.asn1.x509.NameConstraints
+ *
+ */
+public class GeneralSubtree
+ extends ASN1Object
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private GeneralName base;
+
+ private ASN1Integer minimum;
+
+ private ASN1Integer maximum;
+
+ private GeneralSubtree(
+ ASN1Sequence seq)
+ {
+ base = GeneralName.getInstance(seq.getObjectAt(0));
+
+ switch (seq.size())
+ {
+ case 1:
+ break;
+ case 2:
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(1));
+ switch (o.getTagNo())
+ {
+ case 0:
+ minimum = ASN1Integer.getInstance(o, false);
+ break;
+ case 1:
+ maximum = ASN1Integer.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ break;
+ case 3:
+ {
+ {
+ ASN1TaggedObject oMin = ASN1TaggedObject.getInstance(seq.getObjectAt(1));
+ if (oMin.getTagNo() != 0)
+ {
+ throw new IllegalArgumentException("Bad tag number for 'minimum': " + oMin.getTagNo());
+ }
+ minimum = ASN1Integer.getInstance(oMin, false);
+ }
+
+ {
+ ASN1TaggedObject oMax = ASN1TaggedObject.getInstance(seq.getObjectAt(2));
+ if (oMax.getTagNo() != 1)
+ {
+ throw new IllegalArgumentException("Bad tag number for 'maximum': " + oMax.getTagNo());
+ }
+ maximum = ASN1Integer.getInstance(oMax, false);
+ }
+
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * According RFC 3280, the minimum and maximum fields are not used with any
+ * name forms, thus minimum MUST be zero, and maximum MUST be absent.
+ * <p>
+ * If minimum is <code>null</code>, zero is assumed, if
+ * maximum is <code>null</code>, maximum is absent.
+ *
+ * @param base
+ * A restriction.
+ * @param minimum
+ * Minimum
+ *
+ * @param maximum
+ * Maximum
+ */
+ public GeneralSubtree(
+ GeneralName base,
+ BigInteger minimum,
+ BigInteger maximum)
+ {
+ this.base = base;
+ if (maximum != null)
+ {
+ this.maximum = new ASN1Integer(maximum);
+ }
+ if (minimum == null)
+ {
+ this.minimum = null;
+ }
+ else
+ {
+ this.minimum = new ASN1Integer(minimum);
+ }
+ }
+
+ public GeneralSubtree(GeneralName base)
+ {
+ this(base, null, null);
+ }
+
+ public static GeneralSubtree getInstance(
+ ASN1TaggedObject o,
+ boolean explicit)
+ {
+ return new GeneralSubtree(ASN1Sequence.getInstance(o, explicit));
+ }
+
+ public static GeneralSubtree getInstance(
+ Object obj)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+
+ if (obj instanceof GeneralSubtree)
+ {
+ return (GeneralSubtree) obj;
+ }
+
+ return new GeneralSubtree(ASN1Sequence.getInstance(obj));
+ }
+
+ public GeneralName getBase()
+ {
+ return base;
+ }
+
+ public BigInteger getMinimum()
+ {
+ if (minimum == null)
+ {
+ return ZERO;
+ }
+
+ return minimum.getValue();
+ }
+
+ public BigInteger getMaximum()
+ {
+ if (maximum == null)
+ {
+ return null;
+ }
+
+ return maximum.getValue();
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * GeneralSubtree ::= SEQUENCE
+ * {
+ * base GeneralName,
+ * minimum [0] BaseDistance DEFAULT 0,
+ * maximum [1] BaseDistance OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(base);
+
+ if (minimum != null && !minimum.getValue().equals(ZERO))
+ {
+ v.add(new DERTaggedObject(false, 0, minimum));
+ }
+
+ if (maximum != null)
+ {
+ v.add(new DERTaggedObject(false, 1, maximum));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Holder.java b/core/src/main/java/org/spongycastle/asn1/x509/Holder.java
new file mode 100644
index 00000000..e3d89cbd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Holder.java
@@ -0,0 +1,245 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * 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
+ * }
+ * </pre>
+ *
+ * <p>
+ * For an v1 attribute certificate this is:
+ *
+ * <pre>
+ * subject CHOICE {
+ * baseCertificateID [0] EXPLICIT IssuerSerial,
+ * -- associated with a Public Key Certificate
+ * subjectName [1] EXPLICIT GeneralNames },
+ * -- associated with a name
+ * </pre>
+ */
+public class Holder
+ extends ASN1Object
+{
+ public static final int V1_CERTIFICATE_HOLDER = 0;
+ public static final int V2_CERTIFICATE_HOLDER = 1;
+
+ IssuerSerial baseCertificateID;
+
+ GeneralNames entityName;
+
+ ObjectDigestInfo objectDigestInfo;
+
+ private int version = V2_CERTIFICATE_HOLDER;
+
+ public static Holder getInstance(Object obj)
+ {
+ if (obj instanceof Holder)
+ {
+ return (Holder)obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Holder(ASN1TaggedObject.getInstance(obj));
+ }
+ else if (obj != null)
+ {
+ return new Holder(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor for a holder for an V1 attribute certificate.
+ *
+ * @param tagObj The ASN.1 tagged holder object.
+ */
+ private Holder(ASN1TaggedObject tagObj)
+ {
+ switch (tagObj.getTagNo())
+ {
+ case 0:
+ baseCertificateID = IssuerSerial.getInstance(tagObj, true);
+ break;
+ case 1:
+ entityName = GeneralNames.getInstance(tagObj, true);
+ 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.
+ */
+ private Holder(ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(seq
+ .getObjectAt(i));
+
+ switch (tObj.getTagNo())
+ {
+ case 0:
+ baseCertificateID = IssuerSerial.getInstance(tObj, false);
+ break;
+ case 1:
+ entityName = GeneralNames.getInstance(tObj, false);
+ break;
+ case 2:
+ objectDigestInfo = ObjectDigestInfo.getInstance(tObj, false);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag in Holder");
+ }
+ }
+ version = 1;
+ }
+
+ public Holder(IssuerSerial baseCertificateID)
+ {
+ this(baseCertificateID, V2_CERTIFICATE_HOLDER);
+ }
+
+ /**
+ * Constructs a holder from a IssuerSerial for a V1 or V2 certificate.
+ * .
+ * @param baseCertificateID The IssuerSerial.
+ * @param version The version of the attribute certificate.
+ */
+ public Holder(IssuerSerial baseCertificateID, int version)
+ {
+ this.baseCertificateID = baseCertificateID;
+ this.version = version;
+ }
+
+ /**
+ * 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.
+ *
+ * @param entityName The entity or subject name.
+ */
+ public Holder(GeneralNames entityName)
+ {
+ this(entityName, V2_CERTIFICATE_HOLDER);
+ }
+
+ /**
+ * 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 ASN1Primitive toASN1Primitive()
+ {
+ if (version == 1)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ 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
+ {
+ if (entityName != null)
+ {
+ return new DERTaggedObject(true, 1, entityName);
+ }
+ else
+ {
+ return new DERTaggedObject(true, 0, baseCertificateID);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java b/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java
new file mode 100644
index 00000000..c8eca193
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/IetfAttrSyntax.java
@@ -0,0 +1,189 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.DERUTF8String;
+
+/**
+ * Implementation of <code>IetfAttrSyntax</code> as specified by RFC3281.
+ */
+public class IetfAttrSyntax
+ extends ASN1Object
+{
+ 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 static IetfAttrSyntax getInstance(Object obj)
+ {
+ if (obj instanceof IetfAttrSyntax)
+ {
+ return (IetfAttrSyntax)obj;
+ }
+ if (obj != null)
+ {
+ return new IetfAttrSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ */
+ private 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();)
+ {
+ ASN1Primitive obj = (ASN1Primitive)e.nextElement();
+ int type;
+
+ if (obj instanceof ASN1ObjectIdentifier)
+ {
+ 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)
+ {
+ ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1ObjectIdentifier)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 ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java b/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java
new file mode 100644
index 00000000..0ef8674a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/IssuerSerial.java
@@ -0,0 +1,123 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x500.X500Name;
+
+public class IssuerSerial
+ extends ASN1Object
+{
+ GeneralNames issuer;
+ ASN1Integer serial;
+ DERBitString issuerUID;
+
+ public static IssuerSerial getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuerSerial)
+ {
+ return (IssuerSerial)obj;
+ }
+
+ if (obj != null)
+ {
+ return new IssuerSerial(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static IssuerSerial getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ private IssuerSerial(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2 && seq.size() != 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ issuer = GeneralNames.getInstance(seq.getObjectAt(0));
+ serial = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ if (seq.size() == 3)
+ {
+ issuerUID = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public IssuerSerial(
+ X500Name issuer,
+ BigInteger serial)
+ {
+ this(new GeneralNames(new GeneralName(issuer)), new ASN1Integer(serial));
+ }
+
+ public IssuerSerial(
+ GeneralNames issuer,
+ BigInteger serial)
+ {
+ this(issuer, new ASN1Integer(serial));
+ }
+
+ public IssuerSerial(
+ GeneralNames issuer,
+ ASN1Integer serial)
+ {
+ this.issuer = issuer;
+ this.serial = serial;
+ }
+
+ public GeneralNames getIssuer()
+ {
+ return issuer;
+ }
+
+ public ASN1Integer getSerial()
+ {
+ return serial;
+ }
+
+ public DERBitString getIssuerUID()
+ {
+ return issuerUID;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * IssuerSerial ::= SEQUENCE {
+ * issuer GeneralNames,
+ * serial CertificateSerialNumber,
+ * issuerUID UniqueIdentifier OPTIONAL
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(issuer);
+ v.add(serial);
+
+ if (issuerUID != null)
+ {
+ v.add(issuerUID);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java b/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java
new file mode 100644
index 00000000..dd0358fe
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/IssuingDistributionPoint.java
@@ -0,0 +1,274 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * <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 ASN1Object
+{
+ private DistributionPointName distributionPoint;
+
+ 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)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static IssuingDistributionPoint getInstance(
+ Object obj)
+ {
+ if (obj instanceof IssuingDistributionPoint)
+ {
+ return (IssuingDistributionPoint)obj;
+ }
+ else if (obj != null)
+ {
+ return new IssuingDistributionPoint(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * 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)
+ {
+ vec.add(new DERTaggedObject(false, 1, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsCACerts)
+ {
+ vec.add(new DERTaggedObject(false, 2, ASN1Boolean.getInstance(true)));
+ }
+ if (onlySomeReasons != null)
+ {
+ vec.add(new DERTaggedObject(false, 3, onlySomeReasons));
+ }
+ if (indirectCRL)
+ {
+ vec.add(new DERTaggedObject(false, 4, ASN1Boolean.getInstance(true)));
+ }
+ if (onlyContainsAttributeCerts)
+ {
+ vec.add(new DERTaggedObject(false, 5, ASN1Boolean.getInstance(true)));
+ }
+
+ seq = new DERSequence(vec);
+ }
+
+ /**
+ * Shorthand Constructor from given details.
+ *
+ * @param distributionPoint
+ * May contain an URI as pointer to most current CRL.
+ * @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 indirectCRL,
+ boolean onlyContainsAttributeCerts)
+ {
+ this(distributionPoint, false, false, null, indirectCRL, onlyContainsAttributeCerts);
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ */
+ private IssuingDistributionPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ for (int i = 0; i != seq.size(); 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 = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 2:
+ onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 3:
+ onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+ break;
+ case 4:
+ indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ case 5:
+ onlyContainsAttributeCerts = ASN1Boolean.getInstance(o, false).isTrue();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unknown tag in IssuingDistributionPoint");
+ }
+ }
+ }
+
+ public boolean onlyContainsUserCerts()
+ {
+ return onlyContainsUserCerts;
+ }
+
+ public boolean onlyContainsCACerts()
+ {
+ return onlyContainsCACerts;
+ }
+
+ public boolean isIndirectCRL()
+ {
+ return indirectCRL;
+ }
+
+ public boolean onlyContainsAttributeCerts()
+ {
+ return onlyContainsAttributeCerts;
+ }
+
+ /**
+ * @return Returns the distributionPoint.
+ */
+ public DistributionPointName getDistributionPoint()
+ {
+ return distributionPoint;
+ }
+
+ /**
+ * @return Returns the onlySomeReasons.
+ */
+ public ReasonFlags getOnlySomeReasons()
+ {
+ return onlySomeReasons;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java b/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java
new file mode 100644
index 00000000..9308a48a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/KeyPurposeId.java
@@ -0,0 +1,157 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+
+/**
+ * The KeyPurposeId object.
+ * <pre>
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ *
+ * id-kp ::= OBJECT IDENTIFIER { iso(1) identified-organization(3)
+ * dod(6) internet(1) security(5) mechanisms(5) pkix(7) 3}
+ *
+ * </pre>
+ * To create a new KeyPurposeId where none of the below suit, use
+ * <pre>
+ * ASN1ObjectIdentifier newKeyPurposeIdOID = new ASN1ObjectIdentifier("1.3.6.1...");
+ *
+ * KeyPurposeId newKeyPurposeId = KeyPurposeId.getInstance(newKeyPurposeIdOID);
+ * </pre>
+ */
+public class KeyPurposeId
+ extends ASN1Object
+{
+ private static final ASN1ObjectIdentifier id_kp = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.3");
+
+ /**
+ * { 2 5 29 37 0 }
+ */
+ public static final KeyPurposeId anyExtendedKeyUsage = new KeyPurposeId(Extension.extendedKeyUsage.branch("0"));
+
+ /**
+ * { id-kp 1 }
+ */
+ public static final KeyPurposeId id_kp_serverAuth = new KeyPurposeId(id_kp.branch("1"));
+ /**
+ * { id-kp 2 }
+ */
+ public static final KeyPurposeId id_kp_clientAuth = new KeyPurposeId(id_kp.branch("2"));
+ /**
+ * { id-kp 3 }
+ */
+ public static final KeyPurposeId id_kp_codeSigning = new KeyPurposeId(id_kp.branch("3"));
+ /**
+ * { id-kp 4 }
+ */
+ public static final KeyPurposeId id_kp_emailProtection = new KeyPurposeId(id_kp.branch("4"));
+ /**
+ * Usage deprecated by RFC4945 - was { id-kp 5 }
+ */
+ public static final KeyPurposeId id_kp_ipsecEndSystem = new KeyPurposeId(id_kp.branch("5"));
+ /**
+ * Usage deprecated by RFC4945 - was { id-kp 6 }
+ */
+ public static final KeyPurposeId id_kp_ipsecTunnel = new KeyPurposeId(id_kp.branch("6"));
+ /**
+ * Usage deprecated by RFC4945 - was { idkp 7 }
+ */
+ public static final KeyPurposeId id_kp_ipsecUser = new KeyPurposeId(id_kp.branch("7"));
+ /**
+ * { id-kp 8 }
+ */
+ public static final KeyPurposeId id_kp_timeStamping = new KeyPurposeId(id_kp.branch("8"));
+ /**
+ * { id-kp 9 }
+ */
+ public static final KeyPurposeId id_kp_OCSPSigning = new KeyPurposeId(id_kp.branch("9"));
+ /**
+ * { id-kp 10 }
+ */
+ public static final KeyPurposeId id_kp_dvcs = new KeyPurposeId(id_kp.branch("10"));
+ /**
+ * { id-kp 11 }
+ */
+ public static final KeyPurposeId id_kp_sbgpCertAAServerAuth = new KeyPurposeId(id_kp.branch("11"));
+ /**
+ * { id-kp 12 }
+ */
+ public static final KeyPurposeId id_kp_scvp_responder = new KeyPurposeId(id_kp.branch("12"));
+ /**
+ * { id-kp 13 }
+ */
+ public static final KeyPurposeId id_kp_eapOverPPP = new KeyPurposeId(id_kp.branch("13"));
+ /**
+ * { id-kp 14 }
+ */
+ public static final KeyPurposeId id_kp_eapOverLAN = new KeyPurposeId(id_kp.branch("14"));
+ /**
+ * { id-kp 15 }
+ */
+ public static final KeyPurposeId id_kp_scvpServer = new KeyPurposeId(id_kp.branch("15"));
+ /**
+ * { id-kp 16 }
+ */
+ public static final KeyPurposeId id_kp_scvpClient = new KeyPurposeId(id_kp.branch("16"));
+ /**
+ * { id-kp 17 }
+ */
+ public static final KeyPurposeId id_kp_ipsecIKE = new KeyPurposeId(id_kp.branch("17"));
+ /**
+ * { id-kp 18 }
+ */
+ public static final KeyPurposeId id_kp_capwapAC = new KeyPurposeId(id_kp.branch("18"));
+ /**
+ * { id-kp 19 }
+ */
+ public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19"));
+
+ //
+ // microsoft key purpose ids
+ //
+ /**
+ * { 1 3 6 1 4 1 311 20 2 2 }
+ */
+ public static final KeyPurposeId id_kp_smartcardlogon = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"));
+
+ private ASN1ObjectIdentifier id;
+
+ private KeyPurposeId(ASN1ObjectIdentifier id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @deprecated use getInstance and an OID or one of the constants above.
+ * @param id string representation of an OID.
+ */
+ public KeyPurposeId(String id)
+ {
+ this(new ASN1ObjectIdentifier(id));
+ }
+
+ public static KeyPurposeId getInstance(Object o)
+ {
+ if (o instanceof KeyPurposeId)
+ {
+ return (KeyPurposeId)o;
+ }
+ else if (o != null)
+ {
+ return new KeyPurposeId(ASN1ObjectIdentifier.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return id;
+ }
+
+ public String getId()
+ {
+ return id.getId();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java b/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java
new file mode 100644
index 00000000..afff0554
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/KeyUsage.java
@@ -0,0 +1,113 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERBitString;
+
+/**
+ * The KeyUsage object.
+ * <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 KeyUsage
+ extends ASN1Object
+{
+ 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 DERBitString bitString;
+
+ public static KeyUsage getInstance(Object obj) // needs to be DERBitString for other VMs
+ {
+ if (obj instanceof KeyUsage)
+ {
+ return (KeyUsage)obj;
+ }
+ else if (obj != null)
+ {
+ return new KeyUsage(DERBitString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static KeyUsage fromExtensions(Extensions extensions)
+ {
+ return KeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.keyUsage));
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param usage - the bitwise OR of the Key Usage flags giving the
+ * allowed uses for the key.
+ * e.g. (KeyUsage.keyEncipherment | KeyUsage.dataEncipherment)
+ */
+ public KeyUsage(
+ int usage)
+ {
+ this.bitString = new DERBitString(usage);
+ }
+
+ private KeyUsage(
+ DERBitString bitString)
+ {
+ this.bitString = bitString;
+ }
+
+ /**
+ * Return true if a given usage bit is set, false otherwise.
+ *
+ * @param usages combination of usage flags.
+ * @return true if all bits are set, false otherwise.
+ */
+ public boolean hasUsages(int usages)
+ {
+ return (bitString.intValue() & usages) == usages;
+ }
+
+ public byte[] getBytes()
+ {
+ return bitString.getBytes();
+ }
+
+ public int getPadBits()
+ {
+ return bitString.getPadBits();
+ }
+
+ public String toString()
+ {
+ byte[] data = bitString.getBytes();
+
+ if (data.length == 1)
+ {
+ return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff);
+ }
+ return "KeyUsage: 0x" + Integer.toHexString((data[1] & 0xff) << 8 | (data[0] & 0xff));
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return bitString;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java b/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java
new file mode 100644
index 00000000..17611bb4
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/NameConstraints.java
@@ -0,0 +1,118 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+public class NameConstraints
+ extends ASN1Object
+{
+ private GeneralSubtree[] permitted, excluded;
+
+ public static NameConstraints getInstance(Object obj)
+ {
+ if (obj instanceof NameConstraints)
+ {
+ return (NameConstraints)obj;
+ }
+ if (obj != null)
+ {
+ return new NameConstraints(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private NameConstraints(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ switch (o.getTagNo())
+ {
+ case 0:
+ permitted = createArray(ASN1Sequence.getInstance(o, false));
+ break;
+ case 1:
+ excluded = createArray(ASN1Sequence.getInstance(o, false));
+ break;
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * <p>
+ * permitted and excluded are arrays of GeneralSubtree objects.
+ *
+ * @param permitted
+ * Permitted subtrees
+ * @param excluded
+ * Excludes subtrees
+ */
+ public NameConstraints(
+ GeneralSubtree[] permitted,
+ GeneralSubtree[] excluded)
+ {
+ if (permitted != null)
+ {
+ this.permitted = permitted;
+ }
+
+ if (excluded != null)
+ {
+ this.excluded = excluded;
+ }
+ }
+
+ private GeneralSubtree[] createArray(ASN1Sequence subtree)
+ {
+ GeneralSubtree[] ar = new GeneralSubtree[subtree.size()];
+
+ for (int i = 0; i != ar.length; i++)
+ {
+ ar[i] = GeneralSubtree.getInstance(subtree.getObjectAt(i));
+ }
+
+ return ar;
+ }
+
+ public GeneralSubtree[] getPermittedSubtrees()
+ {
+ return permitted;
+ }
+
+ public GeneralSubtree[] getExcludedSubtrees()
+ {
+ return excluded;
+ }
+
+ /*
+ * NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees
+ * OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (permitted != null)
+ {
+ v.add(new DERTaggedObject(false, 0, new DERSequence(permitted)));
+ }
+
+ if (excluded != null)
+ {
+ v.add(new DERTaggedObject(false, 1, new DERSequence(excluded)));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java b/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java
new file mode 100644
index 00000000..5c9a7883
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/NoticeReference.java
@@ -0,0 +1,170 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.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 ASN1Object
+{
+ private DisplayText organization;
+ private ASN1Sequence noticeNumbers;
+
+ private static ASN1EncodableVector convertVector(Vector numbers)
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+
+ Enumeration it = numbers.elements();
+
+ while (it.hasMoreElements())
+ {
+ Object o = it.nextElement();
+ ASN1Integer di;
+
+ if (o instanceof BigInteger)
+ {
+ di = new ASN1Integer((BigInteger)o);
+ }
+ else if (o instanceof Integer)
+ {
+ di = new ASN1Integer(((Integer)o).intValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+
+ av.add(di);
+ }
+ return av;
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization a <code>String</code> value
+ * @param numbers a <code>Vector</code> value
+ */
+ public NoticeReference(
+ String organization,
+ Vector numbers)
+ {
+ this(organization, convertVector(numbers));
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization a <code>String</code> value
+ * @param noticeNumbers an <code>ASN1EncodableVector</code> value
+ */
+ public NoticeReference(
+ String organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this(new DisplayText(organization), noticeNumbers);
+ }
+
+ /**
+ * Creates a new <code>NoticeReference</code> instance.
+ *
+ * @param organization displayText
+ * @param noticeNumbers an <code>ASN1EncodableVector</code> value
+ */
+ public NoticeReference(
+ DisplayText organization,
+ ASN1EncodableVector noticeNumbers)
+ {
+ this.organization = organization;
+ this.noticeNumbers = new DERSequence(noticeNumbers);
+ }
+
+ /**
+ * 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 toASN1Primitive()} for a <code>NoticeReference</code>
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private 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 != null)
+ {
+ return new NoticeReference(ASN1Sequence.getInstance(as));
+ }
+
+ return null;
+ }
+
+ public DisplayText getOrganization()
+ {
+ return organization;
+ }
+
+ public ASN1Integer[] getNoticeNumbers()
+ {
+ ASN1Integer[] tmp = new ASN1Integer[noticeNumbers.size()];
+
+ for (int i = 0; i != noticeNumbers.size(); i++)
+ {
+ tmp[i] = ASN1Integer.getInstance(noticeNumbers.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ /**
+ * Describe <code>toASN1Object</code> method here.
+ *
+ * @return a <code>ASN1Primitive</code> value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+ av.add (organization);
+ av.add (noticeNumbers);
+ return new DERSequence (av);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java b/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java
new file mode 100644
index 00000000..1d714cdf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/ObjectDigestInfo.java
@@ -0,0 +1,190 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * 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 ASN1Object
+{
+ /**
+ * 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;
+
+ ASN1Enumerated digestedObjectType;
+
+ ASN1ObjectIdentifier otherObjectTypeID;
+
+ AlgorithmIdentifier digestAlgorithm;
+
+ DERBitString objectDigest;
+
+ public static ObjectDigestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof ObjectDigestInfo)
+ {
+ return (ObjectDigestInfo)obj;
+ }
+
+ if (obj != null)
+ {
+ return new ObjectDigestInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static ObjectDigestInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ /**
+ * 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,
+ ASN1ObjectIdentifier otherObjectTypeID,
+ AlgorithmIdentifier digestAlgorithm,
+ byte[] objectDigest)
+ {
+ this.digestedObjectType = new ASN1Enumerated(digestedObjectType);
+ if (digestedObjectType == otherObjectDigest)
+ {
+ this.otherObjectTypeID = 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());
+ }
+
+ digestedObjectType = ASN1Enumerated.getInstance(seq.getObjectAt(0));
+
+ int offset = 0;
+
+ if (seq.size() == 4)
+ {
+ otherObjectTypeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(1));
+ offset++;
+ }
+
+ digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
+
+ objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+ }
+
+ public ASN1Enumerated getDigestedObjectType()
+ {
+ return digestedObjectType;
+ }
+
+ public ASN1ObjectIdentifier getOtherObjectTypeID()
+ {
+ return otherObjectTypeID;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ return digestAlgorithm;
+ }
+
+ public DERBitString getObjectDigest()
+ {
+ return objectDigest;
+ }
+
+ /**
+ * 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
+ * }
+ *
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(digestedObjectType);
+
+ if (otherObjectTypeID != null)
+ {
+ v.add(otherObjectTypeID);
+ }
+
+ v.add(digestAlgorithm);
+ v.add(objectDigest);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java b/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java
new file mode 100644
index 00000000..540fd0b2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PolicyConstraints.java
@@ -0,0 +1,106 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * PKIX RFC 5280
+ * <pre>
+ * id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
+ *
+ * PolicyConstraints ::= SEQUENCE {
+ * requireExplicitPolicy [0] SkipCerts OPTIONAL,
+ * inhibitPolicyMapping [1] SkipCerts OPTIONAL }
+ *
+ * SkipCerts ::= INTEGER (0..MAX)
+ * </pre>
+ */
+public class PolicyConstraints
+ extends ASN1Object
+{
+ private BigInteger requireExplicitPolicyMapping;
+ private BigInteger inhibitPolicyMapping;
+
+ public PolicyConstraints(BigInteger requireExplicitPolicyMapping, BigInteger inhibitPolicyMapping)
+ {
+ this.requireExplicitPolicyMapping = requireExplicitPolicyMapping;
+ this.inhibitPolicyMapping = inhibitPolicyMapping;
+ }
+
+ private PolicyConstraints(ASN1Sequence seq)
+ {
+ for (int i = 0; i != seq.size(); i++)
+ {
+ ASN1TaggedObject to = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+ if (to.getTagNo() == 0)
+ {
+ requireExplicitPolicyMapping = ASN1Integer.getInstance(to, false).getValue();
+ }
+ else if (to.getTagNo() == 1)
+ {
+ inhibitPolicyMapping = ASN1Integer.getInstance(to, false).getValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown tag encountered.");
+ }
+ }
+ }
+
+ public static PolicyConstraints getInstance(
+ Object obj)
+ {
+ if (obj instanceof PolicyConstraints)
+ {
+ return (PolicyConstraints)obj;
+ }
+
+ if (obj != null)
+ {
+ return new PolicyConstraints(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static PolicyConstraints fromExtensions(Extensions extensions)
+ {
+ return PolicyConstraints.getInstance(extensions.getExtensionParsedValue(Extension.policyConstraints));
+ }
+
+ public BigInteger getRequireExplicitPolicyMapping()
+ {
+ return requireExplicitPolicyMapping;
+ }
+
+ public BigInteger getInhibitPolicyMapping()
+ {
+ return inhibitPolicyMapping;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (requireExplicitPolicyMapping != null)
+ {
+ v.add(new DERTaggedObject(0, new ASN1Integer(requireExplicitPolicyMapping)));
+ }
+
+ if (inhibitPolicyMapping != null)
+ {
+ v.add(new DERTaggedObject(1, new ASN1Integer(inhibitPolicyMapping)));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java b/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java
new file mode 100644
index 00000000..0b5ee0c8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PolicyInformation.java
@@ -0,0 +1,87 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+public class PolicyInformation
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier policyIdentifier;
+ private ASN1Sequence policyQualifiers;
+
+ private PolicyInformation(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 1 || seq.size() > 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ policyIdentifier = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+
+ if (seq.size() > 1)
+ {
+ policyQualifiers = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+ }
+
+ public PolicyInformation(
+ ASN1ObjectIdentifier policyIdentifier)
+ {
+ this.policyIdentifier = policyIdentifier;
+ }
+
+ public PolicyInformation(
+ ASN1ObjectIdentifier policyIdentifier,
+ ASN1Sequence policyQualifiers)
+ {
+ this.policyIdentifier = policyIdentifier;
+ this.policyQualifiers = policyQualifiers;
+ }
+
+ public static PolicyInformation getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof PolicyInformation)
+ {
+ return (PolicyInformation)obj;
+ }
+
+ return new PolicyInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ public ASN1ObjectIdentifier getPolicyIdentifier()
+ {
+ return policyIdentifier;
+ }
+
+ public ASN1Sequence getPolicyQualifiers()
+ {
+ return policyQualifiers;
+ }
+
+ /*
+ * PolicyInformation ::= SEQUENCE {
+ * policyIdentifier CertPolicyId,
+ * policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ * PolicyQualifierInfo OPTIONAL }
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(policyIdentifier);
+
+ if (policyQualifiers != null)
+ {
+ v.add(policyQualifiers);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java b/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java
new file mode 100644
index 00000000..4b01b0d6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PolicyMappings.java
@@ -0,0 +1,107 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.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 ASN1Object
+{
+ ASN1Sequence seq = null;
+
+ public static PolicyMappings getInstance(Object obj)
+ {
+ if (obj instanceof PolicyMappings)
+ {
+ return (PolicyMappings)obj;
+ }
+ if (obj != null)
+ {
+ return new PolicyMappings(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new <code>PolicyMappings</code> instance.
+ *
+ * @param seq an <code>ASN1Sequence</code> constructed as specified
+ * in RFC 3280
+ */
+ private 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.
+ * @deprecated use CertPolicyId constructors.
+ */
+ 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 ASN1ObjectIdentifier(idp));
+ dv.add(new ASN1ObjectIdentifier(sdp));
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public PolicyMappings(CertPolicyId issuerDomainPolicy, CertPolicyId subjectDomainPolicy)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy);
+ dv.add(subjectDomainPolicy);
+
+ seq = new DERSequence(new DERSequence(dv));
+ }
+
+ public PolicyMappings(CertPolicyId[] issuerDomainPolicy, CertPolicyId[] subjectDomainPolicy)
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+
+ for (int i = 0; i != issuerDomainPolicy.length; i++)
+ {
+ ASN1EncodableVector dv = new ASN1EncodableVector();
+ dv.add(issuerDomainPolicy[i]);
+ dv.add(subjectDomainPolicy[i]);
+ dev.add(new DERSequence(dv));
+ }
+
+ seq = new DERSequence(dev);
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java b/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java
new file mode 100644
index 00000000..011e52cd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierId.java
@@ -0,0 +1,31 @@
+
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * 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 ASN1ObjectIdentifier
+{
+ 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/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java b/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java
new file mode 100644
index 00000000..d7c5a343
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PolicyQualifierInfo.java
@@ -0,0 +1,115 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.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 ASN1Object
+{
+ private ASN1ObjectIdentifier policyQualifierId;
+ private ASN1Encodable 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(
+ ASN1ObjectIdentifier policyQualifierId,
+ ASN1Encodable 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.
+ * @deprecated use PolicyQualifierInfo.getInstance()
+ */
+ public PolicyQualifierInfo(
+ ASN1Sequence as)
+ {
+ if (as.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + as.size());
+ }
+
+ policyQualifierId = ASN1ObjectIdentifier.getInstance(as.getObjectAt(0));
+ qualifier = as.getObjectAt(1);
+ }
+
+ public static PolicyQualifierInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof PolicyQualifierInfo)
+ {
+ return (PolicyQualifierInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new PolicyQualifierInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+
+ public ASN1ObjectIdentifier getPolicyQualifierId()
+ {
+ return policyQualifierId;
+ }
+
+ public ASN1Encodable getQualifier()
+ {
+ return qualifier;
+ }
+
+ /**
+ * Returns a DER-encodable representation of this instance.
+ *
+ * @return a <code>ASN1Primitive</code> value
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector dev = new ASN1EncodableVector();
+ dev.add(policyQualifierId);
+ dev.add(qualifier);
+
+ return new DERSequence(dev);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java b/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java
new file mode 100644
index 00000000..12834c85
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/PrivateKeyUsagePeriod.java
@@ -0,0 +1,84 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * <pre>
+ * PrivateKeyUsagePeriod ::= SEQUENCE {
+ * notBefore [0] GeneralizedTime OPTIONAL,
+ * notAfter [1] GeneralizedTime OPTIONAL }
+ * </pre>
+ */
+public class PrivateKeyUsagePeriod
+ extends ASN1Object
+{
+ public static PrivateKeyUsagePeriod getInstance(Object obj)
+ {
+ if (obj instanceof PrivateKeyUsagePeriod)
+ {
+ return (PrivateKeyUsagePeriod)obj;
+ }
+
+ if (obj != null)
+ {
+ return new PrivateKeyUsagePeriod(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private ASN1GeneralizedTime _notBefore, _notAfter;
+
+ private PrivateKeyUsagePeriod(ASN1Sequence seq)
+ {
+ Enumeration en = seq.getObjects();
+ while (en.hasMoreElements())
+ {
+ ASN1TaggedObject tObj = (ASN1TaggedObject)en.nextElement();
+
+ if (tObj.getTagNo() == 0)
+ {
+ _notBefore = ASN1GeneralizedTime.getInstance(tObj, false);
+ }
+ else if (tObj.getTagNo() == 1)
+ {
+ _notAfter = ASN1GeneralizedTime.getInstance(tObj, false);
+ }
+ }
+ }
+
+ public ASN1GeneralizedTime getNotBefore()
+ {
+ return _notBefore;
+ }
+
+ public ASN1GeneralizedTime getNotAfter()
+ {
+ return _notAfter;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (_notBefore != null)
+ {
+ v.add(new DERTaggedObject(false, 0, _notBefore));
+ }
+ if (_notAfter != null)
+ {
+ v.add(new DERTaggedObject(false, 1, _notAfter));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java b/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java
new file mode 100644
index 00000000..17451164
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/RSAPublicKeyStructure.java
@@ -0,0 +1,98 @@
+package org.spongycastle.asn1.x509;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * @deprecated use org.spongycastle.asn1.pkcs.RSAPublicKey
+ */
+public class RSAPublicKeyStructure
+ extends ASN1Object
+{
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ public static RSAPublicKeyStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPublicKeyStructure getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof RSAPublicKeyStructure)
+ {
+ return (RSAPublicKeyStructure)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new RSAPublicKeyStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid RSAPublicKeyStructure: " + obj.getClass().getName());
+ }
+
+ public RSAPublicKeyStructure(
+ BigInteger modulus,
+ BigInteger publicExponent)
+ {
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ }
+
+ public RSAPublicKeyStructure(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ modulus = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ publicExponent = ASN1Integer.getInstance(e.nextElement()).getPositiveValue();
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * }
+ * </pre>
+ * <p>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(getModulus()));
+ v.add(new ASN1Integer(getPublicExponent()));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java b/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java
new file mode 100644
index 00000000..3df0821b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/ReasonFlags.java
@@ -0,0 +1,85 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.DERBitString;
+
+/**
+ * The ReasonFlags object.
+ * <pre>
+ * ReasonFlags ::= BIT STRING {
+ * unused (0),
+ * keyCompromise (1),
+ * cACompromise (2),
+ * affiliationChanged (3),
+ * superseded (4),
+ * cessationOfOperation (5),
+ * certificateHold (6),
+ * privilegeWithdrawn (7),
+ * aACompromise (8) }
+ * </pre>
+ */
+public class ReasonFlags
+ extends DERBitString
+{
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int UNUSED = (1 << 7);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int KEY_COMPROMISE = (1 << 6);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CA_COMPROMISE = (1 << 5);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AFFILIATION_CHANGED = (1 << 4);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int SUPERSEDED = (1 << 3);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CESSATION_OF_OPERATION = (1 << 2);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int CERTIFICATE_HOLD = (1 << 1);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int PRIVILEGE_WITHDRAWN = (1 << 0);
+ /**
+ * @deprecated use lower case version
+ */
+ public static final int AA_COMPROMISE = (1 << 15);
+
+ public static final int unused = (1 << 7);
+ public static final int keyCompromise = (1 << 6);
+ public static final int cACompromise = (1 << 5);
+ public static final int affiliationChanged = (1 << 4);
+ public static final int superseded = (1 << 3);
+ public static final int cessationOfOperation = (1 << 2);
+ public static final int certificateHold = (1 << 1);
+ public static final int privilegeWithdrawn = (1 << 0);
+ public static final int aACompromise = (1 << 15);
+
+ /**
+ * @param reasons - the bitwise OR of the Key Reason flags giving the
+ * allowed uses for the key.
+ */
+ public ReasonFlags(
+ int reasons)
+ {
+ super(getBytes(reasons), getPadBits(reasons));
+ }
+
+ public ReasonFlags(
+ DERBitString reasons)
+ {
+ super(reasons.getBytes(), reasons.getPadBits());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java b/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java
new file mode 100644
index 00000000..0a0b3d2b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/RoleSyntax.java
@@ -0,0 +1,237 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.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 ASN1Object
+{
+ 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 instanceof RoleSyntax)
+ {
+ return (RoleSyntax)obj;
+ }
+ else if (obj != null)
+ {
+ return new RoleSyntax(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * 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 ||
+ ((ASN1String)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>.
+ */
+ private 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, true);
+ 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()
+ {
+ ASN1String str = (ASN1String)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++)
+ {
+ ASN1Encodable value = names[i].getName();
+ if(value instanceof ASN1String)
+ {
+ namesString[i] = ((ASN1String)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 ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ if(this.roleAuthority != null)
+ {
+ v.add(new DERTaggedObject(false, 0, roleAuthority));
+ }
+ v.add(new DERTaggedObject(true, 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/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java b/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java
new file mode 100644
index 00000000..13d4d284
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/SubjectDirectoryAttributes.java
@@ -0,0 +1,144 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.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.spongycastle.asn1.x500.style.BCStyle for AttributeType ObjectIdentifiers.
+ */
+public class SubjectDirectoryAttributes
+ extends ASN1Object
+{
+ private Vector attributes = new Vector();
+
+ public static SubjectDirectoryAttributes getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectDirectoryAttributes)
+ {
+ return (SubjectDirectoryAttributes)obj;
+ }
+
+ if (obj != null)
+ {
+ return new SubjectDirectoryAttributes(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * 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.
+ */
+ private SubjectDirectoryAttributes(ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
+ attributes.addElement(Attribute.getInstance(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 ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java b/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java
new file mode 100644
index 00000000..2a0272b8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/SubjectKeyIdentifier.java
@@ -0,0 +1,68 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DEROctetString;
+
+/**
+ * The SubjectKeyIdentifier object.
+ * <pre>
+ * SubjectKeyIdentifier::= OCTET STRING
+ * </pre>
+ */
+public class SubjectKeyIdentifier
+ extends ASN1Object
+{
+ private byte[] keyidentifier;
+
+ public static SubjectKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1OctetString.getInstance(obj, explicit));
+ }
+
+ public static SubjectKeyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectKeyIdentifier)
+ {
+ return (SubjectKeyIdentifier)obj;
+ }
+ else if (obj != null)
+ {
+ return new SubjectKeyIdentifier(ASN1OctetString.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static SubjectKeyIdentifier fromExtensions(Extensions extensions)
+ {
+ return SubjectKeyIdentifier.getInstance(extensions.getExtensionParsedValue(Extension.subjectKeyIdentifier));
+ }
+
+ public SubjectKeyIdentifier(
+ byte[] keyid)
+ {
+ this.keyidentifier = keyid;
+ }
+
+ protected SubjectKeyIdentifier(
+ ASN1OctetString keyid)
+ {
+ this.keyidentifier = keyid.getOctets();
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ return keyidentifier;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DEROctetString(keyidentifier);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java b/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java
new file mode 100644
index 00000000..68a4c85e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -0,0 +1,156 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The object that contains the public key stored in a certficate.
+ * <p>
+ * The getEncoded() method in the public keys in the JCE produces a DER
+ * encoded one of these.
+ */
+public class SubjectPublicKeyInfo
+ extends ASN1Object
+{
+ private AlgorithmIdentifier algId;
+ private DERBitString keyData;
+
+ public static SubjectPublicKeyInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static SubjectPublicKeyInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectPublicKeyInfo)
+ {
+ return (SubjectPublicKeyInfo)obj;
+ }
+ else if (obj != null)
+ {
+ return new SubjectPublicKeyInfo(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ ASN1Encodable publicKey)
+ throws IOException
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ byte[] publicKey)
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ this.keyData = DERBitString.getInstance(e.nextElement());
+ }
+
+ public AlgorithmIdentifier getAlgorithm()
+ {
+ return algId;
+ }
+
+ /**
+ * @deprecated use getAlgorithm()
+ * @return alg ID.
+ */
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ /**
+ * for when the public key is an encoded object - if the bitstring
+ * can't be decoded this routine throws an IOException.
+ *
+ * @exception IOException - if the bit string doesn't represent a DER
+ * encoded object.
+ * @return the public key as an ASN.1 primitive.
+ */
+ public ASN1Primitive parsePublicKey()
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes());
+
+ return aIn.readObject();
+ }
+
+ /**
+ * for when the public key is an encoded object - if the bitstring
+ * can't be decoded this routine throws an IOException.
+ *
+ * @exception IOException - if the bit string doesn't represent a DER
+ * encoded object.
+ * @deprecated use parsePublicKey
+ * @return the public key as an ASN.1 primitive.
+ */
+ public ASN1Primitive getPublicKey()
+ throws IOException
+ {
+ ASN1InputStream aIn = new ASN1InputStream(keyData.getBytes());
+
+ return aIn.readObject();
+ }
+
+ /**
+ * for when the public key is raw bits.
+ *
+ * @return the public key as the raw bit string...
+ */
+ public DERBitString getPublicKeyData()
+ {
+ return keyData;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * publicKey BIT STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(algId);
+ v.add(keyData);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java
new file mode 100644
index 00000000..76b46483
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertList.java
@@ -0,0 +1,309 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * PKIX RFC-2459 - TBSCertList object.
+ * <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>
+ */
+public class TBSCertList
+ extends ASN1Object
+{
+ public static class CRLEntry
+ extends ASN1Object
+ {
+ ASN1Sequence seq;
+
+ Extensions crlEntryExtensions;
+
+ private CRLEntry(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 2 || seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ this.seq = seq;
+ }
+
+ public static CRLEntry getInstance(Object o)
+ {
+ if (o instanceof CRLEntry)
+ {
+ return ((CRLEntry)o);
+ }
+ else if (o != null)
+ {
+ return new CRLEntry(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ public ASN1Integer getUserCertificate()
+ {
+ return ASN1Integer.getInstance(seq.getObjectAt(0));
+ }
+
+ public Time getRevocationDate()
+ {
+ return Time.getInstance(seq.getObjectAt(1));
+ }
+
+ public Extensions getExtensions()
+ {
+ if (crlEntryExtensions == null && seq.size() == 3)
+ {
+ crlEntryExtensions = Extensions.getInstance(seq.getObjectAt(2));
+ }
+
+ return crlEntryExtensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+
+ public boolean hasExtensions()
+ {
+ return seq.size() == 3;
+ }
+ }
+
+ 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 CRLEntry.getInstance(en.nextElement());
+ }
+ }
+
+ private class EmptyEnumeration
+ implements Enumeration
+ {
+ public boolean hasMoreElements()
+ {
+ return false;
+ }
+
+ public Object nextElement()
+ {
+ return null; // TODO: check exception handling
+ }
+ }
+
+ ASN1Integer version;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time thisUpdate;
+ Time nextUpdate;
+ ASN1Sequence revokedCertificates;
+ Extensions crlExtensions;
+
+ public static TBSCertList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertList getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertList)
+ {
+ return (TBSCertList)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertList(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TBSCertList(
+ ASN1Sequence seq)
+ {
+ if (seq.size() < 3 || seq.size() > 7)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int seqPos = 0;
+
+ if (seq.getObjectAt(seqPos) instanceof ASN1Integer)
+ {
+ version = ASN1Integer.getInstance(seq.getObjectAt(seqPos++));
+ }
+ else
+ {
+ version = null; // version is optional
+ }
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqPos++));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqPos++));
+ thisUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+
+ if (seqPos < seq.size()
+ && (seq.getObjectAt(seqPos) instanceof ASN1UTCTime
+ || seq.getObjectAt(seqPos) instanceof ASN1GeneralizedTime
+ || seq.getObjectAt(seqPos) instanceof Time))
+ {
+ nextUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+ }
+
+ if (seqPos < seq.size()
+ && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject))
+ {
+ revokedCertificates = ASN1Sequence.getInstance(seq.getObjectAt(seqPos++));
+ }
+
+ if (seqPos < seq.size()
+ && seq.getObjectAt(seqPos) instanceof DERTaggedObject)
+ {
+ crlExtensions = Extensions.getInstance(ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(seqPos), true));
+ }
+ }
+
+ public int getVersionNumber()
+ {
+ if (version == null)
+ {
+ return 1;
+ }
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getThisUpdate()
+ {
+ return thisUpdate;
+ }
+
+ public Time getNextUpdate()
+ {
+ return nextUpdate;
+ }
+
+ public CRLEntry[] getRevokedCertificates()
+ {
+ if (revokedCertificates == null)
+ {
+ return new CRLEntry[0];
+ }
+
+ CRLEntry[] entries = new CRLEntry[revokedCertificates.size()];
+
+ for (int i = 0; i < entries.length; i++)
+ {
+ entries[i] = CRLEntry.getInstance(revokedCertificates.getObjectAt(i));
+ }
+
+ return entries;
+ }
+
+ public Enumeration getRevokedCertificateEnumeration()
+ {
+ if (revokedCertificates == null)
+ {
+ return new EmptyEnumeration();
+ }
+
+ return new RevokedCertificatesEnumeration(revokedCertificates.getObjects());
+ }
+
+ public Extensions getExtensions()
+ {
+ return crlExtensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (version != null)
+ {
+ v.add(version);
+ }
+ v.add(signature);
+ v.add(issuer);
+
+ v.add(thisUpdate);
+ if (nextUpdate != null)
+ {
+ v.add(nextUpdate);
+ }
+
+ // Add CRLEntries if they exist
+ if (revokedCertificates != null)
+ {
+ v.add(revokedCertificates);
+ }
+
+ if (crlExtensions != null)
+ {
+ v.add(new DERTaggedObject(0, crlExtensions));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java
new file mode 100644
index 00000000..b2ab6f1e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificate.java
@@ -0,0 +1,192 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * The TBSCertificate object.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+public class TBSCertificate
+ extends ASN1Object
+{
+ ASN1Sequence seq;
+
+ ASN1Integer version;
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ DERBitString issuerUniqueId;
+ DERBitString subjectUniqueId;
+ Extensions extensions;
+
+ public static TBSCertificate getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertificate getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertificate)
+ {
+ return (TBSCertificate)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertificate(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private TBSCertificate(
+ ASN1Sequence seq)
+ {
+ int seqStart = 0;
+
+ this.seq = seq;
+
+ //
+ // some certficates don't include a version number - we assume v1
+ //
+ if (seq.getObjectAt(0) instanceof DERTaggedObject)
+ {
+ version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+ }
+ else
+ {
+ seqStart = -1; // field 0 is missing!
+ version = new ASN1Integer(0);
+ }
+
+ serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3));
+
+ //
+ // before and after dates
+ //
+ ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);
+
+ startDate = Time.getInstance(dates.getObjectAt(0));
+ endDate = Time.getInstance(dates.getObjectAt(1));
+
+ subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5));
+
+ //
+ // public key info.
+ //
+ subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
+
+ for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+ {
+ DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+ switch (extra.getTagNo())
+ {
+ case 1:
+ issuerUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 2:
+ subjectUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 3:
+ extensions = Extensions.getInstance(ASN1Sequence.getInstance(extra, true));
+ }
+ }
+ }
+
+ public int getVersionNumber()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersion()
+ {
+ return version;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getStartDate()
+ {
+ return startDate;
+ }
+
+ public Time getEndDate()
+ {
+ return endDate;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPublicKeyInfo;
+ }
+
+ public DERBitString getIssuerUniqueId()
+ {
+ return issuerUniqueId;
+ }
+
+ public DERBitString getSubjectUniqueId()
+ {
+ return subjectUniqueId;
+ }
+
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java
new file mode 100644
index 00000000..d138b47e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/TBSCertificateStructure.java
@@ -0,0 +1,194 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * The TBSCertificate object.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+public class TBSCertificateStructure
+ extends ASN1Object
+ implements X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+
+ ASN1Integer version;
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ DERBitString issuerUniqueId;
+ DERBitString subjectUniqueId;
+ X509Extensions extensions;
+
+ public static TBSCertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertificateStructure)
+ {
+ return (TBSCertificateStructure)obj;
+ }
+ else if (obj != null)
+ {
+ return new TBSCertificateStructure(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public TBSCertificateStructure(
+ ASN1Sequence seq)
+ {
+ int seqStart = 0;
+
+ this.seq = seq;
+
+ //
+ // some certficates don't include a version number - we assume v1
+ //
+ if (seq.getObjectAt(0) instanceof DERTaggedObject)
+ {
+ version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+ }
+ else
+ {
+ seqStart = -1; // field 0 is missing!
+ version = new ASN1Integer(0);
+ }
+
+ serialNumber = ASN1Integer.getInstance(seq.getObjectAt(seqStart + 1));
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
+ issuer = X500Name.getInstance(seq.getObjectAt(seqStart + 3));
+
+ //
+ // before and after dates
+ //
+ ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);
+
+ startDate = Time.getInstance(dates.getObjectAt(0));
+ endDate = Time.getInstance(dates.getObjectAt(1));
+
+ subject = X500Name.getInstance(seq.getObjectAt(seqStart + 5));
+
+ //
+ // public key info.
+ //
+ subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
+
+ for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+ {
+ DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+ switch (extra.getTagNo())
+ {
+ case 1:
+ issuerUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 2:
+ subjectUniqueId = DERBitString.getInstance(extra, false);
+ break;
+ case 3:
+ extensions = X509Extensions.getInstance(extra);
+ }
+ }
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public ASN1Integer getVersionNumber()
+ {
+ return version;
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getStartDate()
+ {
+ return startDate;
+ }
+
+ public Time getEndDate()
+ {
+ return endDate;
+ }
+
+ public X500Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPublicKeyInfo;
+ }
+
+ public DERBitString getIssuerUniqueId()
+ {
+ return issuerUniqueId;
+ }
+
+ public DERBitString getSubjectUniqueId()
+ {
+ return subjectUniqueId;
+ }
+
+ public X509Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Target.java b/core/src/main/java/org/spongycastle/asn1/x509/Target.java
new file mode 100644
index 00000000..c153c260
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Target.java
@@ -0,0 +1,138 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERTaggedObject;
+
+/**
+ * Target structure used in target information extension for attribute
+ * certificates from RFC 3281.
+ *
+ * <pre>
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ * </pre>
+ *
+ * <p>
+ * The targetCert field is currently not supported and must not be used
+ * according to RFC 3281.
+ */
+public class Target
+ extends ASN1Object
+ implements ASN1Choice
+{
+ public static final int targetName = 0;
+ public static final int targetGroup = 1;
+
+ private GeneralName targName;
+ private GeneralName targGroup;
+
+ /**
+ * Creates an instance of a Target from the given object.
+ * <p>
+ * <code>obj</code> can be a Target or a {@link ASN1TaggedObject}
+ *
+ * @param obj The object.
+ * @return A Target instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Target getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof Target)
+ {
+ return (Target) obj;
+ }
+ else if (obj instanceof ASN1TaggedObject)
+ {
+ return new Target((ASN1TaggedObject)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: "
+ + obj.getClass());
+ }
+
+ /**
+ * Constructor from ASN1TaggedObject.
+ *
+ * @param tagObj The tagged object.
+ * @throws IllegalArgumentException if the encoding is wrong.
+ */
+ private Target(ASN1TaggedObject tagObj)
+ {
+ switch (tagObj.getTagNo())
+ {
+ case targetName: // GeneralName is already a choice so explicit
+ targName = GeneralName.getInstance(tagObj, true);
+ break;
+ case targetGroup:
+ targGroup = GeneralName.getInstance(tagObj, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown tag: " + tagObj.getTagNo());
+ }
+ }
+
+ /**
+ * Constructor from given details.
+ * <p>
+ * Exactly one of the parameters must be not <code>null</code>.
+ *
+ * @param type the choice type to apply to the name.
+ * @param name the general name.
+ * @throws IllegalArgumentException if type is invalid.
+ */
+ public Target(int type, GeneralName name)
+ {
+ this(new DERTaggedObject(type, name));
+ }
+
+ /**
+ * @return Returns the targetGroup.
+ */
+ public GeneralName getTargetGroup()
+ {
+ return targGroup;
+ }
+
+ /**
+ * @return Returns the targetName.
+ */
+ public GeneralName getTargetName()
+ {
+ return targName;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ // GeneralName is a choice already so most be explicitly tagged
+ if (targName != null)
+ {
+ return new DERTaggedObject(true, 0, targName);
+ }
+ else
+ {
+ return new DERTaggedObject(true, 1, targGroup);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java b/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java
new file mode 100644
index 00000000..0b8c49a5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/TargetInformation.java
@@ -0,0 +1,120 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * Target information extension for attributes certificates according to RFC
+ * 3281.
+ *
+ * <pre>
+ * SEQUENCE OF Targets
+ * </pre>
+ *
+ */
+public class TargetInformation
+ extends ASN1Object
+{
+ private ASN1Sequence targets;
+
+ /**
+ * Creates an instance of a TargetInformation from the given object.
+ * <p>
+ * <code>obj</code> can be a TargetInformation or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A TargetInformation instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as TargetInformation.
+ */
+ public static TargetInformation getInstance(Object obj)
+ {
+ if (obj instanceof TargetInformation)
+ {
+ return (TargetInformation)obj;
+ }
+ else if (obj != null)
+ {
+ return new TargetInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from a ASN1Sequence.
+ *
+ * @param seq The ASN1Sequence.
+ * @throws IllegalArgumentException if the sequence does not contain
+ * correctly encoded Targets elements.
+ */
+ private TargetInformation(ASN1Sequence seq)
+ {
+ targets = seq;
+ }
+
+ /**
+ * Returns the targets in this target information extension.
+ *
+ * @return Returns the targets.
+ */
+ public Targets[] getTargetsObjects()
+ {
+ Targets[] copy = new Targets[targets.size()];
+ int count = 0;
+ for (Enumeration e = targets.getObjects(); e.hasMoreElements();)
+ {
+ copy[count++] = Targets.getInstance(e.nextElement());
+ }
+ return copy;
+ }
+
+ /**
+ * Constructs a target information from a single targets element.
+ * According to RFC 3281 only one targets element must be produced.
+ *
+ * @param targets A Targets instance.
+ */
+ public TargetInformation(Targets targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * According to RFC 3281 only one targets element must be produced. If
+ * multiple targets are given they must be merged in
+ * into one targets element.
+ *
+ * @param targets An array with {@link Targets}.
+ */
+ public TargetInformation(Target[] targets)
+ {
+ this(new Targets(targets));
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * SEQUENCE OF Targets
+ * </pre>
+ *
+ * <p>
+ * According to RFC 3281 only one targets element must be produced. If
+ * multiple targets are given in the constructor they are merged into one
+ * targets element. If this was produced from a
+ * {@link org.spongycastle.asn1.ASN1Sequence} the encoding is kept.
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return targets;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Targets.java b/core/src/main/java/org/spongycastle/asn1/x509/Targets.java
new file mode 100644
index 00000000..a67b6984
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Targets.java
@@ -0,0 +1,121 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * Targets structure used in target information extension for attribute
+ * certificates from RFC 3281.
+ *
+ * <pre>
+ * Targets ::= SEQUENCE OF Target
+ *
+ * Target ::= CHOICE {
+ * targetName [0] GeneralName,
+ * targetGroup [1] GeneralName,
+ * targetCert [2] TargetCert
+ * }
+ *
+ * TargetCert ::= SEQUENCE {
+ * targetCertificate IssuerSerial,
+ * targetName GeneralName OPTIONAL,
+ * certDigestInfo ObjectDigestInfo OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.spongycastle.asn1.x509.Target
+ * @see org.spongycastle.asn1.x509.TargetInformation
+ */
+public class Targets
+ extends ASN1Object
+{
+ private ASN1Sequence targets;
+
+ /**
+ * Creates an instance of a Targets from the given object.
+ * <p>
+ * <code>obj</code> can be a Targets or a {@link ASN1Sequence}
+ *
+ * @param obj The object.
+ * @return A Targets instance.
+ * @throws IllegalArgumentException if the given object cannot be
+ * interpreted as Target.
+ */
+ public static Targets getInstance(Object obj)
+ {
+ if (obj instanceof Targets)
+ {
+ return (Targets)obj;
+ }
+ else if (obj != null)
+ {
+ return new Targets(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * @param targets The ASN.1 SEQUENCE.
+ * @throws IllegalArgumentException if the contents of the sequence are
+ * invalid.
+ */
+ private Targets(ASN1Sequence targets)
+ {
+ this.targets = targets;
+ }
+
+ /**
+ * Constructor from given targets.
+ * <p>
+ * The vector is copied.
+ *
+ * @param targets A <code>Vector</code> of {@link Target}s.
+ * @see Target
+ * @throws IllegalArgumentException if the vector contains not only Targets.
+ */
+ public Targets(Target[] targets)
+ {
+ this.targets = new DERSequence(targets);
+ }
+
+ /**
+ * Returns the targets in a <code>Vector</code>.
+ * <p>
+ * The vector is cloned before it is returned.
+ *
+ * @return Returns the targets.
+ */
+ public Target[] getTargets()
+ {
+ Target[] targs = new Target[targets.size()];
+ int count = 0;
+ for (Enumeration e = targets.getObjects(); e.hasMoreElements();)
+ {
+ targs[count++] = Target.getInstance(e.nextElement());
+ }
+ return targs;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ *
+ * Returns:
+ *
+ * <pre>
+ * Targets ::= SEQUENCE OF Target
+ * </pre>
+ *
+ * @return a ASN1Primitive
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return targets;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/Time.java b/core/src/main/java/org/spongycastle/asn1/x509/Time.java
new file mode 100644
index 00000000..0cda6abf
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/Time.java
@@ -0,0 +1,169 @@
+package org.spongycastle.asn1.x509;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.SimpleTimeZone;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERUTCTime;
+
+public class Time
+ extends ASN1Object
+ implements ASN1Choice
+{
+ ASN1Primitive time;
+
+ public static Time getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject()); // must be explicitly tagged
+ }
+
+ public Time(
+ ASN1Primitive time)
+ {
+ if (!(time instanceof ASN1UTCTime)
+ && !(time instanceof ASN1GeneralizedTime))
+ {
+ 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.
+ *
+ * @param time a date object representing the time of interest.
+ */
+ public Time(
+ Date time)
+ {
+ SimpleTimeZone tz = new SimpleTimeZone(0, "Z");
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+
+ dateF.setTimeZone(tz);
+
+ String d = dateF.format(time) + "Z";
+ int year = Integer.parseInt(d.substring(0, 4));
+
+ if (year < 1950 || year > 2049)
+ {
+ this.time = new DERGeneralizedTime(d);
+ }
+ else
+ {
+ this.time = new DERUTCTime(d.substring(2));
+ }
+ }
+
+ /**
+ * Creates a time object from a given date and locale - if the date is between 1950
+ * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
+ * is used. You may need to use this constructor if the default locale
+ * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
+ *
+ * @param time a date object representing the time of interest.
+ * @param locale an appropriate Locale for producing an ASN.1 GeneralizedTime value.
+ */
+ public Time(
+ Date time,
+ Locale locale)
+ {
+ SimpleTimeZone tz = new SimpleTimeZone(0, "Z");
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss", locale);
+
+ dateF.setTimeZone(tz);
+
+ String d = dateF.format(time) + "Z";
+ int year = Integer.parseInt(d.substring(0, 4));
+
+ if (year < 1950 || year > 2049)
+ {
+ this.time = new DERGeneralizedTime(d);
+ }
+ else
+ {
+ this.time = new DERUTCTime(d.substring(2));
+ }
+ }
+
+ public static Time getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof Time)
+ {
+ return (Time)obj;
+ }
+ else if (obj instanceof ASN1UTCTime)
+ {
+ return new Time((ASN1UTCTime)obj);
+ }
+ else if (obj instanceof ASN1GeneralizedTime)
+ {
+ return new Time((ASN1GeneralizedTime)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+ }
+
+ public String getTime()
+ {
+ if (time instanceof ASN1UTCTime)
+ {
+ return ((ASN1UTCTime)time).getAdjustedTime();
+ }
+ else
+ {
+ return ((ASN1GeneralizedTime)time).getTime();
+ }
+ }
+
+ public Date getDate()
+ {
+ try
+ {
+ if (time instanceof ASN1UTCTime)
+ {
+ return ((ASN1UTCTime)time).getAdjustedDate();
+ }
+ else
+ {
+ return ((ASN1GeneralizedTime)time).getDate();
+ }
+ }
+ catch (ParseException e)
+ { // this should never happen
+ throw new IllegalStateException("invalid date string: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return time;
+ }
+
+ public String toString()
+ {
+ return getTime();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java b/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java
new file mode 100644
index 00000000..22c512c7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/UserNotice.java
@@ -0,0 +1,132 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.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 ASN1Object
+{
+ 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, 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 toASN1Primitive()} for a <code>UserNotice</code>
+ * instance or from parsing it from a DER-encoded stream.
+ */
+ private 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).toASN1Primitive() 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 static UserNotice getInstance(
+ Object obj)
+ {
+ if (obj instanceof UserNotice)
+ {
+ return (UserNotice)obj;
+ }
+
+ if (obj != null)
+ {
+ return new UserNotice(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public NoticeReference getNoticeRef()
+ {
+ return noticeRef;
+ }
+
+ public DisplayText getExplicitText()
+ {
+ return explicitText;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector av = new ASN1EncodableVector();
+
+ if (noticeRef != null)
+ {
+ av.add(noticeRef);
+ }
+
+ if (explicitText != null)
+ {
+ av.add(explicitText);
+ }
+
+ return new DERSequence(av);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java
new file mode 100644
index 00000000..415cabc1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/V1TBSCertificateGenerator.java
@@ -0,0 +1,144 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * 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(true, 0, new ASN1Integer(0));
+
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+
+ public V1TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer.toASN1Primitive());
+ }
+
+ public void setIssuer(
+ X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setStartDate(
+ ASN1UTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setEndDate(
+ ASN1UTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = X500Name.getInstance(subject.toASN1Primitive());
+ }
+
+ public void setSubject(
+ X500Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ public TBSCertificate 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 TBSCertificate.getInstance(new DERSequence(seq));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
new file mode 100644
index 00000000..934e1c88
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/V2AttributeCertificateInfoGenerator.java
@@ -0,0 +1,158 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.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 ASN1Integer version;
+ private Holder holder;
+ private AttCertIssuer issuer;
+ private AlgorithmIdentifier signature;
+ private ASN1Integer serialNumber;
+ private ASN1EncodableVector attributes;
+ private DERBitString issuerUniqueID;
+ private Extensions extensions;
+
+ // Note: validity period start/end dates stored directly
+ //private AttCertValidityPeriod attrCertValidityPeriod;
+ private ASN1GeneralizedTime startDate, endDate;
+
+ public V2AttributeCertificateInfoGenerator()
+ {
+ this.version = new ASN1Integer(1);
+ attributes = new ASN1EncodableVector();
+ }
+
+ public void setHolder(Holder holder)
+ {
+ this.holder = holder;
+ }
+
+ public void addAttribute(String oid, ASN1Encodable value)
+ {
+ attributes.add(new Attribute(new ASN1ObjectIdentifier(oid), new DERSet(value)));
+ }
+
+ /**
+ * @param attribute
+ */
+ public void addAttribute(Attribute attribute)
+ {
+ attributes.add(attribute);
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ public void setIssuer(
+ AttCertIssuer issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ ASN1GeneralizedTime startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(
+ ASN1GeneralizedTime endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setIssuerUniqueID(
+ DERBitString issuerUniqueID)
+ {
+ this.issuerUniqueID = issuerUniqueID;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param extensions
+ */
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ this.extensions = Extensions.getInstance(extensions.toASN1Primitive());
+ }
+
+ public void setExtensions(
+ Extensions 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 AttributeCertificateInfo.getInstance(new DERSequence(v));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java b/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java
new file mode 100644
index 00000000..a94c4a54
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/V2Form.java
@@ -0,0 +1,157 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+
+public class V2Form
+ extends ASN1Object
+{
+ GeneralNames issuerName;
+ IssuerSerial baseCertificateID;
+ ObjectDigestInfo objectDigestInfo;
+
+ public static V2Form getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static V2Form getInstance(
+ Object obj)
+ {
+ if (obj instanceof V2Form)
+ {
+ return (V2Form)obj;
+ }
+ else if (obj != null)
+ {
+ return new V2Form(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public V2Form(
+ GeneralNames issuerName)
+ {
+ this(issuerName, null, null);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ IssuerSerial baseCertificateID)
+ {
+ this(issuerName, baseCertificateID, null);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ ObjectDigestInfo objectDigestInfo)
+ {
+ this(issuerName, null, objectDigestInfo);
+ }
+
+ public V2Form(
+ GeneralNames issuerName,
+ IssuerSerial baseCertificateID,
+ ObjectDigestInfo objectDigestInfo)
+ {
+ this.issuerName = issuerName;
+ this.baseCertificateID = baseCertificateID;
+ this.objectDigestInfo = objectDigestInfo;
+ }
+
+ /**
+ * @deprecated use getInstance().
+ */
+ public V2Form(
+ ASN1Sequence seq)
+ {
+ if (seq.size() > 3)
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + seq.size());
+ }
+
+ int index = 0;
+
+ if (!(seq.getObjectAt(0) instanceof ASN1TaggedObject))
+ {
+ index++;
+ this.issuerName = GeneralNames.getInstance(seq.getObjectAt(0));
+ }
+
+ for (int i = index; i != seq.size(); i++)
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+ if (o.getTagNo() == 0)
+ {
+ baseCertificateID = IssuerSerial.getInstance(o, false);
+ }
+ else if (o.getTagNo() == 1)
+ {
+ objectDigestInfo = ObjectDigestInfo.getInstance(o, false);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad tag number: "
+ + o.getTagNo());
+ }
+ }
+ }
+
+ public GeneralNames getIssuerName()
+ {
+ return issuerName;
+ }
+
+ public IssuerSerial getBaseCertificateID()
+ {
+ return baseCertificateID;
+ }
+
+ public ObjectDigestInfo getObjectDigestInfo()
+ {
+ return objectDigestInfo;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <pre>
+ * V2Form ::= SEQUENCE {
+ * issuerName GeneralNames OPTIONAL,
+ * baseCertificateID [0] IssuerSerial OPTIONAL,
+ * objectDigestInfo [1] ObjectDigestInfo OPTIONAL
+ * -- issuerName MUST be present in this profile
+ * -- baseCertificateID and objectDigestInfo MUST NOT
+ * -- be present in this profile
+ * }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (issuerName != null)
+ {
+ v.add(issuerName);
+ }
+
+ if (baseCertificateID != null)
+ {
+ v.add(new DERTaggedObject(false, 0, baseCertificateID));
+ }
+
+ if (objectDigestInfo != null)
+ {
+ v.add(new DERTaggedObject(false, 1, objectDigestInfo));
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java
new file mode 100644
index 00000000..8419470a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/V2TBSCertListGenerator.java
@@ -0,0 +1,281 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * 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
+{
+ private ASN1Integer version = new ASN1Integer(1);
+ private AlgorithmIdentifier signature;
+ private X500Name issuer;
+ private Time thisUpdate, nextUpdate=null;
+ private Extensions extensions = null;
+ private ASN1EncodableVector crlentries = new ASN1EncodableVector();
+
+ private final static ASN1Sequence[] reasons;
+
+ static
+ {
+ reasons = new ASN1Sequence[11];
+
+ reasons[0] = createReasonExtension(CRLReason.unspecified);
+ reasons[1] = createReasonExtension(CRLReason.keyCompromise);
+ reasons[2] = createReasonExtension(CRLReason.cACompromise);
+ reasons[3] = createReasonExtension(CRLReason.affiliationChanged);
+ reasons[4] = createReasonExtension(CRLReason.superseded);
+ reasons[5] = createReasonExtension(CRLReason.cessationOfOperation);
+ reasons[6] = createReasonExtension(CRLReason.certificateHold);
+ reasons[7] = createReasonExtension(7); // 7 -> unknown
+ reasons[8] = createReasonExtension(CRLReason.removeFromCRL);
+ reasons[9] = createReasonExtension(CRLReason.privilegeWithdrawn);
+ reasons[10] = createReasonExtension(CRLReason.aACompromise);
+ }
+
+ public V2TBSCertListGenerator()
+ {
+ }
+
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer.toASN1Primitive());
+ }
+
+ public void setIssuer(X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setThisUpdate(
+ ASN1UTCTime thisUpdate)
+ {
+ this.thisUpdate = new Time(thisUpdate);
+ }
+
+ public void setNextUpdate(
+ ASN1UTCTime 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)
+ {
+ crlentries.add(crlEntry);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, ASN1UTCTime revocationDate, int reason)
+ {
+ addCRLEntry(userCertificate, new Time(revocationDate), reason);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason)
+ {
+ addCRLEntry(userCertificate, revocationDate, reason, null);
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, int reason, ASN1GeneralizedTime invalidityDate)
+ {
+ if (reason != 0)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ if (reason < reasons.length)
+ {
+ if (reason < 0)
+ {
+ throw new IllegalArgumentException("invalid reason value: " + reason);
+ }
+ v.add(reasons[reason]);
+ }
+ else
+ {
+ v.add(createReasonExtension(reason));
+ }
+
+ if (invalidityDate != null)
+ {
+ v.add(createInvalidityDateExtension(invalidityDate));
+ }
+
+ internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v));
+ }
+ else if (invalidityDate != null)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(createInvalidityDateExtension(invalidityDate));
+
+ internalAddCRLEntry(userCertificate, revocationDate, new DERSequence(v));
+ }
+ else
+ {
+ addCRLEntry(userCertificate, revocationDate, null);
+ }
+ }
+
+ private void internalAddCRLEntry(ASN1Integer userCertificate, Time revocationDate, ASN1Sequence extensions)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(userCertificate);
+ v.add(revocationDate);
+
+ if (extensions != null)
+ {
+ v.add(extensions);
+ }
+
+ addCRLEntry(new DERSequence(v));
+ }
+
+ public void addCRLEntry(ASN1Integer userCertificate, Time revocationDate, Extensions 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)
+ {
+ setExtensions(Extensions.getInstance(extensions));
+ }
+
+ public void setExtensions(
+ Extensions 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.size() != 0)
+ {
+ v.add(new DERSequence(crlentries));
+ }
+
+ if (extensions != null)
+ {
+ v.add(new DERTaggedObject(0, extensions));
+ }
+
+ return new TBSCertList(new DERSequence(v));
+ }
+
+ private static ASN1Sequence createReasonExtension(int reasonCode)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ CRLReason crlReason = CRLReason.lookup(reasonCode);
+
+ try
+ {
+ v.add(Extension.reasonCode);
+ v.add(new DEROctetString(crlReason.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding reason: " + e);
+ }
+
+ return new DERSequence(v);
+ }
+
+ private static ASN1Sequence createInvalidityDateExtension(ASN1GeneralizedTime invalidityDate)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ try
+ {
+ v.add(Extension.invalidityDate);
+ v.add(new DEROctetString(invalidityDate.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding reason: " + e);
+ }
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java
new file mode 100644
index 00000000..26328cc5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -0,0 +1,212 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1UTCTime;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * Generator for Version 3 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ *
+ */
+public class V3TBSCertificateGenerator
+{
+ DERTaggedObject version = new DERTaggedObject(true, 0, new ASN1Integer(2));
+
+ ASN1Integer serialNumber;
+ AlgorithmIdentifier signature;
+ X500Name issuer;
+ Time startDate, endDate;
+ X500Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ Extensions extensions;
+
+ private boolean altNamePresentAndCritical;
+ private DERBitString issuerUniqueID;
+ private DERBitString subjectUniqueID;
+
+ public V3TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ ASN1Integer serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = X500Name.getInstance(issuer);
+ }
+
+ public void setIssuer(
+ X500Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ ASN1UTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(
+ ASN1UTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ /**
+ * @deprecated use X500Name method
+ */
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = X500Name.getInstance(subject.toASN1Primitive());
+ }
+
+ public void setSubject(
+ X500Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setIssuerUniqueID(
+ DERBitString uniqueID)
+ {
+ this.issuerUniqueID = uniqueID;
+ }
+
+ public void setSubjectUniqueID(
+ DERBitString uniqueID)
+ {
+ this.subjectUniqueID = uniqueID;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ /**
+ * @deprecated use method taking Extensions
+ * @param extensions
+ */
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ setExtensions(Extensions.getInstance(extensions));
+ }
+
+ public void setExtensions(
+ Extensions extensions)
+ {
+ this.extensions = extensions;
+ if (extensions != null)
+ {
+ Extension altName = extensions.getExtension(Extension.subjectAlternativeName);
+
+ if (altName != null && altName.isCritical())
+ {
+ altNamePresentAndCritical = true;
+ }
+ }
+ }
+
+ public TBSCertificate generateTBSCertificate()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+ }
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(version);
+ v.add(serialNumber);
+ v.add(signature);
+ v.add(issuer);
+
+ //
+ // before and after dates
+ //
+ ASN1EncodableVector validity = new ASN1EncodableVector();
+
+ validity.add(startDate);
+ validity.add(endDate);
+
+ v.add(new DERSequence(validity));
+
+ 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(true, 3, extensions));
+ }
+
+ return TBSCertificate.getInstance(new DERSequence(v));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java b/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java
new file mode 100644
index 00000000..28669a71
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509AttributeIdentifiers.java
@@ -0,0 +1,29 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface X509AttributeIdentifiers
+{
+ /**
+ * @deprecated use id_at_role
+ */
+ static final ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72");
+
+ static final ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4");
+ static final ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6");
+ static final ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10");
+
+ static final ASN1ObjectIdentifier id_ce_targetInformation= X509ObjectIdentifiers.id_ce.branch("55");
+
+ static final ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10");
+
+ static final ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1");
+ static final ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2");
+ static final ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3");
+ static final ASN1ObjectIdentifier id_aca_group = id_aca.branch("4");
+ // { id-aca 5 } is reserved
+ static final ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6");
+
+ static final ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72");
+ static final ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55");
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java b/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java
new file mode 100644
index 00000000..b7511792
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509CertificateStructure.java
@@ -0,0 +1,129 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ * @deprecated use org.spongycastle.asn1.x509.Certificate
+ */
+public class X509CertificateStructure
+ extends ASN1Object
+ implements X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+ TBSCertificateStructure tbsCert;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static X509CertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509CertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof X509CertificateStructure)
+ {
+ return (X509CertificateStructure)obj;
+ }
+ else if (obj != null)
+ {
+ return new X509CertificateStructure(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public X509CertificateStructure(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ //
+ // correct x509 certficate
+ //
+ if (seq.size() == 3)
+ {
+ tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+
+ sig = DERBitString.getInstance(seq.getObjectAt(2));
+ }
+ else
+ {
+ throw new IllegalArgumentException("sequence wrong size for a certificate");
+ }
+ }
+
+ public TBSCertificateStructure getTBSCertificate()
+ {
+ return tbsCert;
+ }
+
+ public int getVersion()
+ {
+ return tbsCert.getVersion();
+ }
+
+ public ASN1Integer getSerialNumber()
+ {
+ return tbsCert.getSerialNumber();
+ }
+
+ public X500Name getIssuer()
+ {
+ return tbsCert.getIssuer();
+ }
+
+ public Time getStartDate()
+ {
+ return tbsCert.getStartDate();
+ }
+
+ public Time getEndDate()
+ {
+ return tbsCert.getEndDate();
+ }
+
+ public X500Name getSubject()
+ {
+ return tbsCert.getSubject();
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return tbsCert.getSubjectPublicKeyInfo();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return seq;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java b/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java
new file mode 100644
index 00000000..38685b41
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -0,0 +1,65 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERUTF8String;
+
+/**
+ * The default converter for X509 DN entries when going from their
+ * string value to ASN.1 strings.
+ */
+public class X509DefaultEntryConverter
+ extends X509NameEntryConverter
+{
+ /**
+ * Apply default coversion for the given value depending on the oid
+ * and the character range of the value.
+ *
+ * @param oid the object identifier for the DN entry
+ * @param value the value associated with it
+ * @return the ASN.1 equivalent for the string value.
+ */
+ public ASN1Primitive getConvertedValue(
+ ASN1ObjectIdentifier oid,
+ String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return convertHexEncoded(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+ else
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java b/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java
new file mode 100644
index 00000000..6537d80f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509Extension.java
@@ -0,0 +1,249 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ * @deprecated use Extension
+ */
+public class X509Extension
+{
+ /**
+ * Subject Directory Attributes
+ */
+ public static final ASN1ObjectIdentifier subjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ */
+ public static final ASN1ObjectIdentifier subjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ */
+ public static final ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ */
+ public static final ASN1ObjectIdentifier privateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ */
+ public static final ASN1ObjectIdentifier subjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ */
+ public static final ASN1ObjectIdentifier issuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ */
+ public static final ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ */
+ public static final ASN1ObjectIdentifier cRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ */
+ public static final ASN1ObjectIdentifier reasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ */
+ public static final ASN1ObjectIdentifier instructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ */
+ public static final ASN1ObjectIdentifier invalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ */
+ public static final ASN1ObjectIdentifier deltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ */
+ public static final ASN1ObjectIdentifier issuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ */
+ public static final ASN1ObjectIdentifier certificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ */
+ public static final ASN1ObjectIdentifier nameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ */
+ public static final ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ */
+ public static final ASN1ObjectIdentifier certificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ */
+ public static final ASN1ObjectIdentifier policyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ */
+ public static final ASN1ObjectIdentifier authorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ */
+ public static final ASN1ObjectIdentifier policyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ */
+ public static final ASN1ObjectIdentifier extendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ */
+ public static final ASN1ObjectIdentifier freshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ */
+ public static final ASN1ObjectIdentifier inhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ */
+ public static final ASN1ObjectIdentifier authorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ */
+ public static final ASN1ObjectIdentifier subjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ */
+ public static final ASN1ObjectIdentifier logoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ */
+ public static final ASN1ObjectIdentifier biometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ */
+ public static final ASN1ObjectIdentifier qCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier auditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier noRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ */
+ public static final ASN1ObjectIdentifier targetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ boolean critical;
+ ASN1OctetString value;
+
+ public X509Extension(
+ ASN1Boolean critical,
+ ASN1OctetString value)
+ {
+ this.critical = critical.isTrue();
+ this.value = value;
+ }
+
+ public X509Extension(
+ boolean critical,
+ ASN1OctetString value)
+ {
+ this.critical = critical;
+ this.value = value;
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public ASN1OctetString getValue()
+ {
+ return value;
+ }
+
+ public ASN1Encodable getParsedValue()
+ {
+ return convertValueToObject(this);
+ }
+
+ public int hashCode()
+ {
+ if (this.isCritical())
+ {
+ return this.getValue().hashCode();
+ }
+
+ return ~this.getValue().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof X509Extension))
+ {
+ return false;
+ }
+
+ X509Extension other = (X509Extension)o;
+
+ 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 ASN1Primitive convertValueToObject(
+ X509Extension ext)
+ throws IllegalArgumentException
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(ext.getValue().getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't convert extension: " + e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java b/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java
new file mode 100644
index 00000000..fb2cf923
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509Extensions.java
@@ -0,0 +1,477 @@
+package org.spongycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Boolean;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * @deprecated use Extensions
+ */
+public class X509Extensions
+ extends ASN1Object
+{
+ /**
+ * Subject Directory Attributes
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectDirectoryAttributes = new ASN1ObjectIdentifier("2.5.29.9");
+
+ /**
+ * Subject Key Identifier
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier KeyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PrivateKeyUsagePeriod = new ASN1ObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectAlternativeName = new ASN1ObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier IssuerAlternativeName = new ASN1ObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier BasicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CRLNumber = new ASN1ObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier ReasonCode = new ASN1ObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InstructionCode = new ASN1ObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InvalidityDate = new ASN1ObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier DeltaCRLIndicator = new ASN1ObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier IssuingDistributionPoint = new ASN1ObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CertificateIssuer = new ASN1ObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier NameConstraints = new ASN1ObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier CertificatePolicies = new ASN1ObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PolicyMappings = new ASN1ObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuthorityKeyIdentifier = new ASN1ObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier PolicyConstraints = new ASN1ObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier ExtendedKeyUsage = new ASN1ObjectIdentifier("2.5.29.37");
+
+ /**
+ * Freshest CRL
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier FreshestCRL = new ASN1ObjectIdentifier("2.5.29.46");
+
+ /**
+ * Inhibit Any Policy
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier InhibitAnyPolicy = new ASN1ObjectIdentifier("2.5.29.54");
+
+ /**
+ * Authority Info Access
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuthorityInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.1");
+
+ /**
+ * Subject Info Access
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier SubjectInfoAccess = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.11");
+
+ /**
+ * Logo Type
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier LogoType = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.12");
+
+ /**
+ * BiometricInfo
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier BiometricInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.2");
+
+ /**
+ * QCStatements
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier QCStatements = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.3");
+
+ /**
+ * Audit identity extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier AuditIdentity = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.4");
+
+ /**
+ * NoRevAvail extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier NoRevAvail = new ASN1ObjectIdentifier("2.5.29.56");
+
+ /**
+ * TargetInformation extension in attribute certificates.
+ * @deprecated use X509Extension value.
+ */
+ public static final ASN1ObjectIdentifier TargetInformation = new ASN1ObjectIdentifier("2.5.29.55");
+
+ private Hashtable extensions = new Hashtable();
+ private Vector ordering = new Vector();
+
+ public static X509Extensions getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Extensions getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Extensions)
+ {
+ return (X509Extensions)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new X509Extensions((ASN1Sequence)obj);
+ }
+
+ if (obj instanceof Extensions)
+ {
+ return new X509Extensions((ASN1Sequence)((Extensions)obj).toASN1Primitive());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ *
+ * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+ */
+ public X509Extensions(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(e.nextElement());
+
+ if (s.size() == 3)
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension(ASN1Boolean.getInstance(s.getObjectAt(1)), ASN1OctetString.getInstance(s.getObjectAt(2))));
+ }
+ else if (s.size() == 2)
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension(false, ASN1OctetString.getInstance(s.getObjectAt(1))));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Bad sequence size: " + s.size());
+ }
+
+ ordering.addElement(s.getObjectAt(0));
+ }
+ }
+
+ /**
+ * constructor from a table of extensions.
+ * <p>
+ * it's is assumed the table contains OID/String pairs.
+ */
+ public X509Extensions(
+ Hashtable extensions)
+ {
+ this(null, extensions);
+ }
+
+ /**
+ * Constructor from a table of extensions with ordering.
+ * <p>
+ * It's is assumed the table contains OID/String pairs.
+ * @deprecated use Extensions
+ */
+ public X509Extensions(
+ Vector ordering,
+ Hashtable extensions)
+ {
+ Enumeration e;
+
+ if (ordering == null)
+ {
+ e = extensions.keys();
+ }
+ else
+ {
+ e = ordering.elements();
+ }
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(ASN1ObjectIdentifier.getInstance(e.nextElement()));
+ }
+
+ e = this.ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(e.nextElement());
+ X509Extension ext = (X509Extension)extensions.get(oid);
+
+ this.extensions.put(oid, ext);
+ }
+ }
+
+ /**
+ * Constructor from two vectors
+ *
+ * @param objectIDs a vector of the object identifiers.
+ * @param values a vector of the extension values.
+ * @deprecated use Extensions
+ */
+ public X509Extensions(
+ Vector objectIDs,
+ Vector values)
+ {
+ Enumeration e = objectIDs.elements();
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ }
+
+ int count = 0;
+
+ e = this.ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)values.elementAt(count);
+
+ this.extensions.put(oid, ext);
+ count++;
+ }
+ }
+
+ /**
+ * return an Enumeration of the extension field's object ids.
+ */
+ public Enumeration oids()
+ {
+ return ordering.elements();
+ }
+
+ /**
+ * return the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the extension if it's present, null otherwise.
+ */
+ public X509Extension getExtension(
+ ASN1ObjectIdentifier oid)
+ {
+ return (X509Extension)extensions.get(oid);
+ }
+
+ /**
+ * <pre>
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnId EXTENSION.&amp;id ({ExtensionSet}),
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ Enumeration e = ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)extensions.get(oid);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oid);
+
+ if (ext.isCritical())
+ {
+ v.add(ASN1Boolean.TRUE);
+ }
+
+ v.add(ext.getValue());
+
+ vec.add(new DERSequence(v));
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public boolean equivalent(
+ X509Extensions other)
+ {
+ if (extensions.size() != other.extensions.size())
+ {
+ return false;
+ }
+
+ Enumeration e1 = extensions.keys();
+
+ while (e1.hasMoreElements())
+ {
+ Object key = e1.nextElement();
+
+ if (!extensions.get(key).equals(other.extensions.get(key)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public ASN1ObjectIdentifier[] getExtensionOIDs()
+ {
+ return toOidArray(ordering);
+ }
+
+ public ASN1ObjectIdentifier[] getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public ASN1ObjectIdentifier[] getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical)
+ {
+ Vector oidVec = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ Object oid = ordering.elementAt(i);
+
+ if (((X509Extension)extensions.get(oid)).isCritical() == isCritical)
+ {
+ oidVec.addElement(oid);
+ }
+ }
+
+ return toOidArray(oidVec);
+ }
+
+ private ASN1ObjectIdentifier[] toOidArray(Vector oidVec)
+ {
+ ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[oidVec.size()];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ oids[i] = (ASN1ObjectIdentifier)oidVec.elementAt(i);
+ }
+ return oids;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java b/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java
new file mode 100644
index 00000000..af8fc9bb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509ExtensionsGenerator.java
@@ -0,0 +1,94 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DEROctetString;
+
+/**
+ * Generator for X.509 extensions
+ * @deprecated use org.spongycastle.asn1.x509.ExtensionsGenerator
+ */
+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(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ {
+ try
+ {
+ this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.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(
+ ASN1ObjectIdentifier 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/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java b/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java
new file mode 100644
index 00000000..7d2fedf8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509Name.java
@@ -0,0 +1,1379 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERUniversalString;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
+
+/**
+ * <pre>
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY }
+ * </pre>
+ * @deprecated use org.spongycastle.asn1.x500.X500Name.
+ */
+public class X509Name
+ extends ASN1Object
+{
+ /**
+ * country code - StringType(SIZE(2))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5");
+
+ /**
+ * street - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier SERIALNUMBER = SN;
+
+ /**
+ * locality name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7");
+
+ /**
+ * state, or province name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8");
+
+ /**
+ * Naming attributes of type X520name
+ */
+ public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * businessCategory - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier(
+ "2.5.4.15");
+
+ /**
+ * postalCode - DirectoryString(SIZE(1..40)
+ */
+ public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier(
+ "2.5.4.17");
+
+ /**
+ * dnQualifier - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier(
+ "2.5.4.46");
+
+ /**
+ * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier(
+ "2.5.4.65");
+
+
+ /**
+ * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
+ */
+ public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.1");
+
+ /**
+ * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
+ */
+ public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.2");
+
+ /**
+ * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
+ */
+ public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.3");
+
+ /**
+ * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.4");
+
+ /**
+ * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
+ * codes only
+ */
+ public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier(
+ "1.3.6.1.5.5.7.9.5");
+
+
+ /**
+ * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
+ */
+ public static final ASN1ObjectIdentifier NAME_AT_BIRTH = new ASN1ObjectIdentifier("1.3.36.8.3.14");
+
+ /**
+ * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
+ * DirectoryString(SIZE(1..30))
+ */
+ public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16");
+
+ /**
+ * RFC 2256 dmdName
+ */
+ public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54");
+
+ /**
+ * id-at-telephoneNumber
+ */
+ public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
+
+ /**
+ * id-at-name
+ */
+ public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
+
+ /**
+ * Email address (RSA PKCS#9 extension) - IA5String.
+ * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ * @deprecated use a X500NameStyle
+ */
+ public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
+
+ /**
+ * more from PKCS#9
+ */
+ public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
+ public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final ASN1ObjectIdentifier E = EmailAddress;
+
+ /*
+ * others...
+ */
+ public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /**
+ * LDAP User id.
+ */
+ public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /**
+ * determines whether or not strings should be processed and printed
+ * from back to front.
+ */
+ public static boolean DefaultReverse = false;
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ 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 final Hashtable RFC2253Symbols = new Hashtable();
+
+ /**
+ * look up table translating OID values into their common symbols following the convention in RFC 1779
+ *
+ */
+ public static final Hashtable RFC1779Symbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ 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;
+
+ private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
+ private static final Boolean FALSE = new Boolean(false);
+
+ static
+ {
+ DefaultSymbols.put(C, "C");
+ DefaultSymbols.put(O, "O");
+ DefaultSymbols.put(T, "T");
+ DefaultSymbols.put(OU, "OU");
+ DefaultSymbols.put(CN, "CN");
+ DefaultSymbols.put(L, "L");
+ DefaultSymbols.put(ST, "ST");
+ DefaultSymbols.put(SN, "SERIALNUMBER");
+ DefaultSymbols.put(EmailAddress, "E");
+ DefaultSymbols.put(DC, "DC");
+ DefaultSymbols.put(UID, "UID");
+ DefaultSymbols.put(STREET, "STREET");
+ DefaultSymbols.put(SURNAME, "SURNAME");
+ DefaultSymbols.put(GIVENNAME, "GIVENNAME");
+ DefaultSymbols.put(INITIALS, "INITIALS");
+ DefaultSymbols.put(GENERATION, "GENERATION");
+ DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
+ DefaultSymbols.put(UnstructuredName, "unstructuredName");
+ DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
+ DefaultSymbols.put(DN_QUALIFIER, "DN");
+ DefaultSymbols.put(PSEUDONYM, "Pseudonym");
+ DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
+ DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
+ DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
+ DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
+ DefaultSymbols.put(GENDER, "Gender");
+ DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
+ 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");
+ RFC2253Symbols.put(OU, "OU");
+ RFC2253Symbols.put(CN, "CN");
+ RFC2253Symbols.put(L, "L");
+ RFC2253Symbols.put(ST, "ST");
+ RFC2253Symbols.put(STREET, "STREET");
+ RFC2253Symbols.put(DC, "DC");
+ RFC2253Symbols.put(UID, "UID");
+
+ RFC1779Symbols.put(C, "C");
+ RFC1779Symbols.put(O, "O");
+ RFC1779Symbols.put(OU, "OU");
+ RFC1779Symbols.put(CN, "CN");
+ RFC1779Symbols.put(L, "L");
+ RFC1779Symbols.put(ST, "ST");
+ RFC1779Symbols.put(STREET, "STREET");
+
+ DefaultLookUp.put("c", C);
+ DefaultLookUp.put("o", O);
+ DefaultLookUp.put("t", T);
+ DefaultLookUp.put("ou", OU);
+ DefaultLookUp.put("cn", CN);
+ DefaultLookUp.put("l", L);
+ DefaultLookUp.put("st", ST);
+ DefaultLookUp.put("sn", SN);
+ DefaultLookUp.put("serialnumber", SN);
+ DefaultLookUp.put("street", STREET);
+ DefaultLookUp.put("emailaddress", E);
+ DefaultLookUp.put("dc", DC);
+ DefaultLookUp.put("e", E);
+ DefaultLookUp.put("uid", UID);
+ DefaultLookUp.put("surname", SURNAME);
+ DefaultLookUp.put("givenname", GIVENNAME);
+ DefaultLookUp.put("initials", INITIALS);
+ DefaultLookUp.put("generation", GENERATION);
+ DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
+ DefaultLookUp.put("unstructuredname", UnstructuredName);
+ DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
+ DefaultLookUp.put("dn", DN_QUALIFIER);
+ DefaultLookUp.put("pseudonym", PSEUDONYM);
+ DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
+ DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
+ DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
+ DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
+ DefaultLookUp.put("gender", GENDER);
+ DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
+ 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;
+ private Vector ordering = new Vector();
+ private Vector values = new Vector();
+ private Vector added = new Vector();
+
+ private ASN1Sequence seq;
+
+ private boolean isHashCodeCalculated;
+ private int hashCodeValue;
+
+ /**
+ * Return a X509Name based on the passed in tagged object.
+ *
+ * @param obj tag object holding name.
+ * @param explicit true if explicitly tagged false otherwise.
+ * @return the X509Name
+ */
+ public static X509Name getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Name getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Name)
+ {
+ return (X509Name)obj;
+ }
+ else if (obj instanceof X500Name)
+ {
+ return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive()));
+ }
+ else if (obj != null)
+ {
+ return new X509Name(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ protected X509Name()
+ {
+ // constructure use by new X500 Name class
+ }
+ /**
+ * Constructor from ASN1Sequence
+ *
+ * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+ * @deprecated use X500Name.getInstance()
+ */
+ public X509Name(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Set set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+ for (int i = 0; i < set.size(); i++)
+ {
+ ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
+
+ if (s.size() != 2)
+ {
+ throw new IllegalArgumentException("badly sized pair");
+ }
+
+ ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
+
+ ASN1Encodable value = s.getObjectAt(1);
+ if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+ {
+ String v = ((ASN1String)value).getString();
+ if (v.length() > 0 && v.charAt(0) == '#')
+ {
+ values.addElement("\\" + v);
+ }
+ else
+ {
+ values.addElement(v);
+ }
+ }
+ else
+ {
+ try
+ {
+ values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
+ }
+ catch (IOException e1)
+ {
+ throw new IllegalArgumentException("cannot encode value");
+ }
+ }
+ added.addElement((i != 0) ? TRUE : FALSE); // to allow earlier JDK compatibility
+ }
+ }
+ }
+
+ /**
+ * constructor from a table of attributes.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process.
+ * <p>
+ * <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)
+ {
+ this(null, attributes);
+ }
+
+ /**
+ * Constructor from a table of attributes with ordering.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process. The ordering vector should contain the OIDs
+ * in the order they are meant to be encoded or printed in toString.
+ */
+ public X509Name(
+ Vector ordering,
+ Hashtable attributes)
+ {
+ this(ordering, attributes, new X509DefaultEntryConverter());
+ }
+
+ /**
+ * Constructor from a table of attributes with ordering.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process. The ordering vector should contain the OIDs
+ * in the order they are meant to be encoded or printed in toString.
+ * <p>
+ * The passed in converter will be used to convert the strings into their
+ * ASN.1 counterparts.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector ordering,
+ Hashtable attributes,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+
+ if (ordering != null)
+ {
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ this.ordering.addElement(ordering.elementAt(i));
+ this.added.addElement(FALSE);
+ }
+ }
+ else
+ {
+ Enumeration e = attributes.keys();
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ this.added.addElement(FALSE);
+ }
+ }
+
+ for (int i = 0; i != this.ordering.size(); i++)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)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
+ }
+ }
+
+ /**
+ * Takes two vectors one of the oids and the other of the values.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector oids,
+ Vector values)
+ {
+ this(oids, values, new X509DefaultEntryConverter());
+ }
+
+ /**
+ * Takes two vectors one of the oids and the other of the values.
+ * <p>
+ * The passed in converter will be used to convert the strings into their
+ * ASN.1 counterparts.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ Vector oids,
+ Vector values,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+
+ if (oids.size() != values.size())
+ {
+ throw new IllegalArgumentException("oids vector must be same length as values.");
+ }
+
+ for (int i = 0; i < oids.size(); i++)
+ {
+ this.ordering.addElement(oids.elementAt(i));
+ this.values.addElement(values.elementAt(i));
+ this.added.addElement(FALSE);
+ }
+ }
+
+// 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.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * 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 with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(DefaultReverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * 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. If reverse
+ * is true, create the encoded version of the sequence starting from the
+ * last element in the string.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName)
+ {
+ this(reverse, DefaultLookUp, dirName);
+ }
+
+ /**
+ * 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 with each
+ * string value being converted to its associated ASN.1 type using the passed
+ * in converter. If reverse is true the ASN.1 sequence representing the DN will
+ * be built by starting at the end of the string, rather than the start.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this(reverse, DefaultLookUp, dirName, converter);
+ }
+
+ /**
+ * 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. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically.
+ * <br>
+ * If reverse is true, create the encoded version of the sequence
+ * starting from the last element in the string.
+ * @param reverse true if we should start scanning from the end (RFC 2553).
+ * @param lookUp table of names and their oids.
+ * @param dirName the X.500 string to be parsed.
+ * @deprecated use X500Name, X500NameBuilder
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName)
+ {
+ this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
+ }
+
+ private ASN1ObjectIdentifier decodeOID(
+ String name,
+ Hashtable lookUp)
+ {
+ name = name.trim();
+ if (Strings.toUpperCase(name).startsWith("OID."))
+ {
+ return new ASN1ObjectIdentifier(name.substring(4));
+ }
+ else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
+ {
+ return new ASN1ObjectIdentifier(name);
+ }
+
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
+ if (oid == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
+ }
+
+ return oid;
+ }
+
+ private String unescape(String elt)
+ {
+ if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+ {
+ return elt.trim();
+ }
+
+ char[] elts = elt.toCharArray();
+ boolean escaped = false;
+ boolean quoted = false;
+ StringBuffer buf = new StringBuffer(elt.length());
+ int start = 0;
+
+ // if it's an escaped hash string and not an actual encoding in string form
+ // we need to leave it escaped.
+ if (elts[0] == '\\')
+ {
+ if (elts[1] == '#')
+ {
+ start = 2;
+ buf.append("\\#");
+ }
+ }
+
+ boolean nonWhiteSpaceEncountered = false;
+ int lastEscaped = 0;
+
+ for (int i = start; i != elts.length; i++)
+ {
+ char c = elts[i];
+
+ if (c != ' ')
+ {
+ nonWhiteSpaceEncountered = true;
+ }
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else if (c == '\\' && !(escaped || quoted))
+ {
+ escaped = true;
+ lastEscaped = buf.length();
+ }
+ else
+ {
+ if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
+ {
+ continue;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ }
+
+ if (buf.length() > 0)
+ {
+ while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
+ {
+ buf.setLength(buf.length() - 1);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * 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. lookUp
+ * should provide a table of lookups, indexed by lowercase only strings and
+ * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
+ * will be processed automatically. The passed in converter is used to convert the
+ * string values to the right of each equals sign to their ASN.1 counterparts.
+ * <br>
+ * @param reverse true if we should start scanning from the end, false otherwise.
+ * @param lookUp table of names and oids.
+ * @param dirName the string dirName
+ * @param converter the converter to convert string values into their ASN.1 equivalents
+ */
+ public X509Name(
+ boolean reverse,
+ Hashtable lookUp,
+ String dirName,
+ X509NameEntryConverter converter)
+ {
+ this.converter = converter;
+ X509NameTokenizer nTok = new X509NameTokenizer(dirName);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+
+ if (token.indexOf('+') > 0)
+ {
+ X509NameTokenizer pTok = new X509NameTokenizer(token, '+');
+
+ addEntry(lookUp, pTok.nextToken(), FALSE);
+
+ while (pTok.hasMoreTokens())
+ {
+ addEntry(lookUp, pTok.nextToken(), TRUE);
+ }
+ }
+ else
+ {
+ addEntry(lookUp, token, FALSE);
+ }
+ }
+
+ 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;
+ }
+ }
+
+ private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
+ {
+ X509NameTokenizer vTok;
+ String name;
+ String value;ASN1ObjectIdentifier oid;
+ vTok = new X509NameTokenizer(token, '=');
+
+ name = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ value = vTok.nextToken();
+
+ oid = decodeOID(name, lookUp);
+
+ this.ordering.addElement(oid);
+ this.values.addElement(unescape(value));
+ this.added.addElement(isAdded);
+ }
+
+ /**
+ * return a vector of the oids in the name, in the order they were found.
+ */
+ public Vector getOIDs()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ v.addElement(ordering.elementAt(i));
+ }
+
+ return v;
+ }
+
+ /**
+ * return a vector of the values found in the name, in the order they
+ * were found.
+ */
+ public Vector getValues()
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ v.addElement(values.elementAt(i));
+ }
+
+ 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(
+ ASN1ObjectIdentifier oid)
+ {
+ Vector v = new Vector();
+
+ for (int i = 0; i != values.size(); i++)
+ {
+ if (ordering.elementAt(i).equals(oid))
+ {
+ String val = (String)values.elementAt(i);
+
+ if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
+ {
+ v.addElement(val.substring(1));
+ }
+ else
+ {
+ v.addElement(val);
+ }
+ }
+ }
+
+ return v;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (seq == null)
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ ASN1EncodableVector sVec = new ASN1EncodableVector();
+ ASN1ObjectIdentifier lstOid = null;
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+
+ v.add(oid);
+
+ String str = (String)values.elementAt(i);
+
+ v.add(converter.getConvertedValue(oid, str));
+
+ if (lstOid == null
+ || ((Boolean)this.added.elementAt(i)).booleanValue())
+ {
+ sVec.add(new DERSequence(v));
+ }
+ else
+ {
+ vec.add(new DERSet(sVec));
+ sVec = new ASN1EncodableVector();
+
+ sVec.add(new DERSequence(v));
+ }
+
+ lstOid = oid;
+ }
+
+ vec.add(new DERSet(sVec));
+
+ seq = new DERSequence(vec);
+ }
+
+ return seq;
+ }
+
+ /**
+ * @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)
+ {
+ if (!inOrder)
+ {
+ return this.equals(obj);
+ }
+
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < orderingSize; i++)
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
+
+ if (oid.equals(oOid))
+ {
+ String value = (String)values.elementAt(i);
+ String oValue = (String)other.values.elementAt(i);
+
+ if (!equivalentStrings(value, oValue))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ if (isHashCodeCalculated)
+ {
+ return hashCodeValue;
+ }
+
+ isHashCodeCalculated = true;
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != ordering.size(); i += 1)
+ {
+ String value = (String)values.elementAt(i);
+
+ value = canonicalize(value);
+ value = stripInternalSpaces(value);
+
+ hashCodeValue ^= ordering.elementAt(i).hashCode();
+ hashCodeValue ^= value.hashCode();
+ }
+
+ return hashCodeValue;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ X509Name other;
+
+ try
+ {
+ other = X509Name.getInstance(obj);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+
+ int orderingSize = ordering.size();
+
+ if (orderingSize != other.ordering.size())
+ {
+ return false;
+ }
+
+ boolean[] indexes = new boolean[orderingSize];
+ int start, end, delta;
+
+ if (ordering.elementAt(0).equals(other.ordering.elementAt(0))) // guess forward
+ {
+ 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;
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
+ String value = (String)values.elementAt(i);
+
+ for (int j = 0; j < orderingSize; j++)
+ {
+ if (indexes[j])
+ {
+ continue;
+ }
+
+ ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
+
+ if (oid.equals(oOid))
+ {
+ String oValue = (String)other.values.elementAt(j);
+
+ if (equivalentStrings(value, oValue))
+ {
+ indexes[j] = true;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean equivalentStrings(String s1, String s2)
+ {
+ String value = canonicalize(s1);
+ String oValue = canonicalize(s2);
+
+ if (!value.equals(oValue))
+ {
+ value = stripInternalSpaces(value);
+ oValue = stripInternalSpaces(oValue);
+
+ if (!value.equals(oValue))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private String canonicalize(String s)
+ {
+ String value = Strings.toLowerCase(s.trim());
+
+ if (value.length() > 0 && value.charAt(0) == '#')
+ {
+ ASN1Primitive obj = decodeObject(value);
+
+ if (obj instanceof ASN1String)
+ {
+ value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
+ }
+ }
+
+ return value;
+ }
+
+ private ASN1Primitive decodeObject(String oValue)
+ {
+ try
+ {
+ return ASN1Primitive.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(
+ StringBuffer buf,
+ Hashtable oidSymbols,
+ ASN1ObjectIdentifier oid,
+ String value)
+ {
+ String sym = (String)oidSymbols.get(oid);
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ }
+
+ buf.append('=');
+
+ int start = buf.length();
+ buf.append(value);
+ int end = buf.length();
+
+ if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
+ {
+ start += 2;
+ }
+
+ while (start < end && buf.charAt(start) == ' ')
+ {
+ buf.insert(start, "\\");
+ start += 2;
+ ++end;
+ }
+
+ while (--end > start && buf.charAt(end) == ' ')
+ {
+ buf.insert(end, '\\');
+ }
+
+ while (start <= end)
+ {
+ switch (buf.charAt(start))
+ {
+ case ',':
+ case '"':
+ case '\\':
+ case '+':
+ case '=':
+ case '<':
+ case '>':
+ case ';':
+ buf.insert(start, "\\");
+ start += 2;
+ ++end;
+ break;
+ default:
+ ++start;
+ break;
+ }
+ }
+ }
+
+ /**
+ * convert the structure to a string - if reverse is true the
+ * oids and values are listed out starting with the last element
+ * in the sequence (ala RFC 2253), otherwise the string will begin
+ * with the first element of the structure. If no string definition
+ * for the oid is found in oidSymbols the string value of the oid is
+ * added. Two standard symbol tables are provided DefaultSymbols, and
+ * RFC2253Symbols as part of this class.
+ *
+ * @param reverse if true start at the end of the sequence and work back.
+ * @param oidSymbols look up table strings for oids.
+ */
+ public String toString(
+ boolean reverse,
+ Hashtable oidSymbols)
+ {
+ StringBuffer buf = new StringBuffer();
+ Vector components = new Vector();
+ boolean first = true;
+
+ StringBuffer ava = null;
+
+ for (int i = 0; i < ordering.size(); i++)
+ {
+ if (((Boolean)added.elementAt(i)).booleanValue())
+ {
+ ava.append('+');
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ }
+ else
+ {
+ ava = new StringBuffer();
+ appendValue(ava, oidSymbols,
+ (ASN1ObjectIdentifier)ordering.elementAt(i),
+ (String)values.elementAt(i));
+ components.addElement(ava);
+ }
+ }
+
+ if (reverse)
+ {
+ for (int i = components.size() - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+ else
+ {
+ for (int i = 0; i < components.size(); i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ buf.append(components.elementAt(i).toString());
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private String bytesToString(
+ byte[] data)
+ {
+ char[] cs = new char[data.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(data[i] & 0xff);
+ }
+
+ return new String(cs);
+ }
+
+ public String toString()
+ {
+ return toString(DefaultReverse, DefaultSymbols);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java b/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java
new file mode 100644
index 00000000..bcbcc28e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509NameEntryConverter.java
@@ -0,0 +1,114 @@
+package org.spongycastle.asn1.x509;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.util.Strings;
+
+/**
+ * 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
+ * number of machines on the internet. By default the X509Name class
+ * will produce UTF8Strings in line with the current recommendations (RFC 3280).
+ * <p>
+ * An example of an encoder look like below:
+ * <pre>
+ * public class X509DirEntryConverter
+ * extends X509NameEntryConverter
+ * {
+ * public ASN1Primitive getConvertedValue(
+ * ASN1ObjectIdentifier oid,
+ * String value)
+ * {
+ * if (str.length() != 0 &amp;&amp; str.charAt(0) == '#')
+ * {
+ * return convertHexEncoded(str, 1);
+ * }
+ * if (oid.equals(EmailAddress))
+ * {
+ * return new DERIA5String(str);
+ * }
+ * else if (canBePrintable(str))
+ * {
+ * return new DERPrintableString(str);
+ * }
+ * else if (canBeUTF8(str))
+ * {
+ * return new DERUTF8String(str);
+ * }
+ * else
+ * {
+ * return new DERBMPString(str);
+ * }
+ * }
+ * }
+ * </pre>
+ */
+public abstract class X509NameEntryConverter
+{
+ /**
+ * Convert an inline encoded hex string rendition of an ASN.1
+ * object back into its corresponding ASN.1 object.
+ *
+ * @param str the hex encoded object
+ * @param off the index at which the encoding starts
+ * @return the decoded object
+ */
+ protected ASN1Primitive convertHexEncoded(
+ String str,
+ int off)
+ throws IOException
+ {
+ str = Strings.toLowerCase(str);
+ byte[] data = new byte[(str.length() - off) / 2];
+ for (int index = 0; index != data.length; index++)
+ {
+ char left = str.charAt((index * 2) + off);
+ char right = str.charAt((index * 2) + off + 1);
+
+ if (left < 'a')
+ {
+ data[index] = (byte)((left - '0') << 4);
+ }
+ else
+ {
+ data[index] = (byte)((left - 'a' + 10) << 4);
+ }
+ if (right < 'a')
+ {
+ data[index] |= (byte)(right - '0');
+ }
+ else
+ {
+ data[index] |= (byte)(right - 'a' + 10);
+ }
+ }
+
+ ASN1InputStream aIn = new ASN1InputStream(data);
+
+ return aIn.readObject();
+ }
+
+ /**
+ * return true if the passed in String can be represented without
+ * loss as a PrintableString, false otherwise.
+ */
+ protected boolean canBePrintable(
+ String str)
+ {
+ return DERPrintableString.isPrintableString(str);
+ }
+
+ /**
+ * Convert the passed in String value into the appropriate ASN.1
+ * encoded object.
+ *
+ * @param oid the oid associated with the value in the DN.
+ * @param value the value of the particular DN component.
+ * @return the ASN.1 equivalent for the value.
+ */
+ public abstract ASN1Primitive getConvertedValue(ASN1ObjectIdentifier oid, String value);
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java b/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java
new file mode 100644
index 00000000..864f0e81
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509NameTokenizer.java
@@ -0,0 +1,91 @@
+package org.spongycastle.asn1.x509;
+
+/**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ * @deprecated use X500NameTokenizer
+ */
+public class X509NameTokenizer
+{
+ private String value;
+ private int index;
+ private char separator;
+ private StringBuffer buf = new StringBuffer();
+
+ public X509NameTokenizer(
+ String oid)
+ {
+ this(oid, ',');
+ }
+
+ public X509NameTokenizer(
+ String oid,
+ char separator)
+ {
+ this.value = oid;
+ this.index = -1;
+ this.separator = separator;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != value.length());
+ }
+
+ public String nextToken()
+ {
+ if (index == value.length())
+ {
+ return null;
+ }
+
+ int end = index + 1;
+ boolean quoted = false;
+ boolean escaped = false;
+
+ buf.setLength(0);
+
+ while (end != value.length())
+ {
+ char c = value.charAt(end);
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ buf.append(c);
+ escaped = false;
+ }
+ else
+ {
+ if (escaped || quoted)
+ {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (c == '\\')
+ {
+ buf.append(c);
+ escaped = true;
+ }
+ else if (c == separator)
+ {
+ break;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ end++;
+ }
+
+ index = end;
+
+ return buf.toString();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java b/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java
new file mode 100644
index 00000000..a760411b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -0,0 +1,81 @@
+package org.spongycastle.asn1.x509;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface X509ObjectIdentifiers
+{
+
+ /** Subject RDN components: commonName = 2.5.4.3 */
+ static final ASN1ObjectIdentifier commonName = new ASN1ObjectIdentifier("2.5.4.3");
+ /** Subject RDN components: countryName = 2.5.4.6 */
+ static final ASN1ObjectIdentifier countryName = new ASN1ObjectIdentifier("2.5.4.6");
+ /** Subject RDN components: localityName = 2.5.4.7 */
+ static final ASN1ObjectIdentifier localityName = new ASN1ObjectIdentifier("2.5.4.7");
+ /** Subject RDN components: stateOrProvinceName = 2.5.4.8 */
+ static final ASN1ObjectIdentifier stateOrProvinceName = new ASN1ObjectIdentifier("2.5.4.8");
+ /** Subject RDN components: organization = 2.5.4.10 */
+ static final ASN1ObjectIdentifier organization = new ASN1ObjectIdentifier("2.5.4.10");
+ /** Subject RDN components: organizationalUnitName = 2.5.4.11 */
+ static final ASN1ObjectIdentifier organizationalUnitName = new ASN1ObjectIdentifier("2.5.4.11");
+
+ /** Subject RDN components: telephone_number = 2.5.4.20 */
+ static final ASN1ObjectIdentifier id_at_telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20");
+ /** Subject RDN components: name = 2.5.4.41 */
+ static final ASN1ObjectIdentifier id_at_name = new ASN1ObjectIdentifier("2.5.4.41");
+
+ /**
+ * id-SHA1 OBJECT IDENTIFIER ::=
+ * {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }
+ * <p>
+ * OID: 1.3.14.3.2.27
+ */
+ static final ASN1ObjectIdentifier id_SHA1 = new ASN1ObjectIdentifier("1.3.14.3.2.26");
+
+ /**
+ * ripemd160 OBJECT IDENTIFIER ::=
+ * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
+ * <p>
+ * OID: 1.3.36.3.2.1
+ */
+ static final ASN1ObjectIdentifier ripemd160 = new ASN1ObjectIdentifier("1.3.36.3.2.1");
+
+ /**
+ * ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
+ * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
+ * <p>
+ * OID: 1.3.36.3.3.1.2
+ */
+ static final ASN1ObjectIdentifier ripemd160WithRSAEncryption = new ASN1ObjectIdentifier("1.3.36.3.3.1.2");
+
+
+ /** OID: 2.5.8.1.1 */
+ static final ASN1ObjectIdentifier id_ea_rsa = new ASN1ObjectIdentifier("2.5.8.1.1");
+
+ /** id-pkix OID: 1.3.6.1.5.5.7
+ */
+ static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+
+ /**
+ * private internet extensions; OID = 1.3.6.1.5.5.7.1
+ */
+ static final ASN1ObjectIdentifier id_pe = id_pkix.branch("1");
+
+ /**
+ * ISO ARC for standard certificate and CRL extensions
+ * <p>
+ * OID: 2.5.29
+ */
+ static final ASN1ObjectIdentifier id_ce = new ASN1ObjectIdentifier("2.5.29");
+
+ /** id-pkix OID: 1.3.6.1.5.5.7.48 */
+ static final ASN1ObjectIdentifier id_ad = id_pkix.branch("48");
+ /** id-ad-caIssuers OID: 1.3.6.1.5.5.7.48.2 */
+ static final ASN1ObjectIdentifier id_ad_caIssuers = id_ad.branch("2");
+ /** id-ad-ocsp OID: 1.3.6.1.5.5.7.48.1 */
+ static final ASN1ObjectIdentifier id_ad_ocsp = id_ad.branch("1");
+
+ /** OID for ocsp uri in AuthorityInformationAccess extension */
+ static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
+ /** OID for crl uri in AuthorityInformationAccess extension */
+ static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers;
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java
new file mode 100644
index 00000000..6175572c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/BiometricData.java
@@ -0,0 +1,122 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * The BiometricData object.
+ * <pre>
+ * BiometricData ::= SEQUENCE {
+ * typeOfBiometricData TypeOfBiometricData,
+ * hashAlgorithm AlgorithmIdentifier,
+ * biometricDataHash OCTET STRING,
+ * sourceDataUri IA5String OPTIONAL }
+ * </pre>
+ */
+public class BiometricData
+ extends ASN1Object
+{
+ private TypeOfBiometricData typeOfBiometricData;
+ private AlgorithmIdentifier hashAlgorithm;
+ private ASN1OctetString biometricDataHash;
+ private DERIA5String sourceDataUri;
+
+ public static BiometricData getInstance(
+ Object obj)
+ {
+ if (obj instanceof BiometricData)
+ {
+ return (BiometricData)obj;
+ }
+
+ if (obj != null)
+ {
+ return new BiometricData(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private 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 ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
new file mode 100644
index 00000000..50145669
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
@@ -0,0 +1,11 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface ETSIQCObjectIdentifiers
+{
+ static final ASN1ObjectIdentifier id_etsi_qcs_QcCompliance = new ASN1ObjectIdentifier("0.4.0.1862.1.1");
+ static final ASN1ObjectIdentifier id_etsi_qcs_LimiteValue = new ASN1ObjectIdentifier("0.4.0.1862.1.2");
+ static final ASN1ObjectIdentifier id_etsi_qcs_RetentionPeriod = new ASN1ObjectIdentifier("0.4.0.1862.1.3");
+ static final ASN1ObjectIdentifier id_etsi_qcs_QcSSCD = new ASN1ObjectIdentifier("0.4.0.1862.1.4");
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
new file mode 100644
index 00000000..3e791b47
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/Iso4217CurrencyCode.java
@@ -0,0 +1,93 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.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 ASN1Object
+ implements ASN1Choice
+{
+ final int ALPHABETIC_MAXSIZE = 3;
+ final int NUMERIC_MINSIZE = 1;
+ final int NUMERIC_MAXSIZE = 999;
+
+ ASN1Encodable obj;
+ int numeric;
+
+ public static Iso4217CurrencyCode getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof Iso4217CurrencyCode)
+ {
+ return (Iso4217CurrencyCode)obj;
+ }
+
+ if (obj instanceof ASN1Integer)
+ {
+ ASN1Integer numericobj = ASN1Integer.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 ASN1Integer(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 ((ASN1Integer)obj).getValue().intValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return obj.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java
new file mode 100644
index 00000000..edf1897a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/MonetaryValue.java
@@ -0,0 +1,92 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The MonetaryValue object.
+ * <pre>
+ * MonetaryValue ::= SEQUENCE {
+ * currency Iso4217CurrencyCode,
+ * amount INTEGER,
+ * exponent INTEGER }
+ * -- value = amount * 10^exponent
+ * </pre>
+ */
+public class MonetaryValue
+ extends ASN1Object
+{
+ private Iso4217CurrencyCode currency;
+ private ASN1Integer amount;
+ private ASN1Integer exponent;
+
+ public static MonetaryValue getInstance(
+ Object obj)
+ {
+ if (obj instanceof MonetaryValue)
+ {
+ return (MonetaryValue)obj;
+ }
+
+ if (obj != null)
+ {
+ return new MonetaryValue(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private MonetaryValue(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+ // currency
+ currency = Iso4217CurrencyCode.getInstance(e.nextElement());
+ // hashAlgorithm
+ amount = ASN1Integer.getInstance(e.nextElement());
+ // exponent
+ exponent = ASN1Integer.getInstance(e.nextElement());
+ }
+
+ public MonetaryValue(
+ Iso4217CurrencyCode currency,
+ int amount,
+ int exponent)
+ {
+ this.currency = currency;
+ this.amount = new ASN1Integer(amount);
+ this.exponent = new ASN1Integer(exponent);
+ }
+
+ public Iso4217CurrencyCode getCurrency()
+ {
+ return currency;
+ }
+
+ public BigInteger getAmount()
+ {
+ return amount.getValue();
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent.getValue();
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(currency);
+ seq.add(amount);
+ seq.add(exponent);
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java
new file mode 100644
index 00000000..f66f5113
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/QCStatement.java
@@ -0,0 +1,95 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+
+/**
+ * The QCStatement object.
+ * <pre>
+ * QCStatement ::= SEQUENCE {
+ * statementId OBJECT IDENTIFIER,
+ * statementInfo ANY DEFINED BY statementId OPTIONAL}
+ * </pre>
+ */
+
+public class QCStatement
+ extends ASN1Object
+ implements ETSIQCObjectIdentifiers, RFC3739QCObjectIdentifiers
+{
+ ASN1ObjectIdentifier qcStatementId;
+ ASN1Encodable qcStatementInfo;
+
+ public static QCStatement getInstance(
+ Object obj)
+ {
+ if (obj instanceof QCStatement)
+ {
+ return (QCStatement)obj;
+ }
+ if (obj != null)
+ {
+ return new QCStatement(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private QCStatement(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ // qcStatementId
+ qcStatementId = ASN1ObjectIdentifier.getInstance(e.nextElement());
+ // qcstatementInfo
+ if (e.hasMoreElements())
+ {
+ qcStatementInfo = (ASN1Encodable) e.nextElement();
+ }
+ }
+
+ public QCStatement(
+ ASN1ObjectIdentifier qcStatementId)
+ {
+ this.qcStatementId = qcStatementId;
+ this.qcStatementInfo = null;
+ }
+
+ public QCStatement(
+ ASN1ObjectIdentifier qcStatementId,
+ ASN1Encodable qcStatementInfo)
+ {
+ this.qcStatementId = qcStatementId;
+ this.qcStatementInfo = qcStatementInfo;
+ }
+
+ public ASN1ObjectIdentifier getStatementId()
+ {
+ return qcStatementId;
+ }
+
+ public ASN1Encodable getStatementInfo()
+ {
+ return qcStatementInfo;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector seq = new ASN1EncodableVector();
+ seq.add(qcStatementId);
+
+ if (qcStatementInfo != null)
+ {
+ seq.add(qcStatementInfo);
+ }
+
+ return new DERSequence(seq);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
new file mode 100644
index 00000000..d3663b0e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
@@ -0,0 +1,11 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+public interface RFC3739QCObjectIdentifiers
+{
+ /** OID: 1.3.6.1.5.5.7.11.1 */
+ static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v1 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.1");
+ /** OID: 1.3.6.1.5.5.7.11.2 */
+ static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v2 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.2");
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java
new file mode 100644
index 00000000..2a52a7d9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/SemanticsInformation.java
@@ -0,0 +1,131 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.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 ASN1Object
+{
+ private ASN1ObjectIdentifier semanticsIdentifier;
+ private GeneralName[] nameRegistrationAuthorities;
+
+ public static SemanticsInformation getInstance(Object obj)
+ {
+ if (obj instanceof SemanticsInformation)
+ {
+ return (SemanticsInformation)obj;
+ }
+
+ if (obj != null)
+ {
+ return new SemanticsInformation(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ private 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 ASN1ObjectIdentifier)
+ {
+ semanticsIdentifier = ASN1ObjectIdentifier.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(
+ ASN1ObjectIdentifier semanticsIdentifier,
+ GeneralName[] generalNames)
+ {
+ this.semanticsIdentifier = semanticsIdentifier;
+ this.nameRegistrationAuthorities = generalNames;
+ }
+
+ public SemanticsInformation(ASN1ObjectIdentifier semanticsIdentifier)
+ {
+ this.semanticsIdentifier = semanticsIdentifier;
+ this.nameRegistrationAuthorities = null;
+ }
+
+ public SemanticsInformation(GeneralName[] generalNames)
+ {
+ this.semanticsIdentifier = null;
+ this.nameRegistrationAuthorities = generalNames;
+ }
+
+ public ASN1ObjectIdentifier getSemanticsIdentifier()
+ {
+ return semanticsIdentifier;
+ }
+
+ public GeneralName[] getNameRegistrationAuthorities()
+ {
+ return nameRegistrationAuthorities;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ 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/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java b/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java
new file mode 100644
index 00000000..e7285d26
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/qualified/TypeOfBiometricData.java
@@ -0,0 +1,90 @@
+package org.spongycastle.asn1.x509.qualified;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+
+/**
+ * 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 ASN1Object
+ implements ASN1Choice
+{
+ public static final int PICTURE = 0;
+ public static final int HANDWRITTEN_SIGNATURE = 1;
+
+ ASN1Encodable obj;
+
+ public static TypeOfBiometricData getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof TypeOfBiometricData)
+ {
+ return (TypeOfBiometricData)obj;
+ }
+
+ if (obj instanceof ASN1Integer)
+ {
+ ASN1Integer predefinedBiometricTypeObj = ASN1Integer.getInstance(obj);
+ int predefinedBiometricType = predefinedBiometricTypeObj.getValue().intValue();
+
+ return new TypeOfBiometricData(predefinedBiometricType);
+ }
+ else if (obj instanceof ASN1ObjectIdentifier)
+ {
+ ASN1ObjectIdentifier BiometricDataID = ASN1ObjectIdentifier.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 ASN1Integer(predefinedBiometricType);
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknow PredefinedBiometricType : " + predefinedBiometricType);
+ }
+ }
+
+ public TypeOfBiometricData(ASN1ObjectIdentifier BiometricDataID)
+ {
+ obj = BiometricDataID;
+ }
+
+ public boolean isPredefined()
+ {
+ return obj instanceof ASN1Integer;
+ }
+
+ public int getPredefinedBiometricType()
+ {
+ return ((ASN1Integer)obj).getValue().intValue();
+ }
+
+ public ASN1ObjectIdentifier getBiometricDataOid()
+ {
+ return (ASN1ObjectIdentifier)obj;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return obj.toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java b/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java
new file mode 100644
index 00000000..72433a0e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/sigi/NameOrPseudonym.java
@@ -0,0 +1,189 @@
+package org.spongycastle.asn1.x509.sigi;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x500.DirectoryString;
+
+/**
+ * Structure for a name or pseudonym.
+ *
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @see org.spongycastle.asn1.x509.sigi.PersonalData
+ *
+ */
+public class NameOrPseudonym
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private DirectoryString pseudonym;
+
+ private DirectoryString surname;
+
+ private ASN1Sequence givenName;
+
+ public static NameOrPseudonym getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof NameOrPseudonym)
+ {
+ return (NameOrPseudonym)obj;
+ }
+
+ if (obj instanceof ASN1String)
+ {
+ return new NameOrPseudonym(DirectoryString.getInstance(obj));
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new NameOrPseudonym((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from DirectoryString.
+ * <p>
+ * The sequence is of type NameOrPseudonym:
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ * @param pseudonym pseudonym value to use.
+ */
+ public NameOrPseudonym(DirectoryString pseudonym)
+ {
+ this.pseudonym = pseudonym;
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type NameOrPseudonym:
+ * <p/>
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private NameOrPseudonym(ASN1Sequence seq)
+ {
+ if (seq.size() != 2)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ if (!(seq.getObjectAt(0) instanceof ASN1String))
+ {
+ throw new IllegalArgumentException("Bad object encountered: "
+ + seq.getObjectAt(0).getClass());
+ }
+
+ surname = DirectoryString.getInstance(seq.getObjectAt(0));
+ givenName = ASN1Sequence.getInstance(seq.getObjectAt(1));
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param pseudonym The pseudonym.
+ */
+ public NameOrPseudonym(String pseudonym)
+ {
+ this(new DirectoryString(pseudonym));
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param surname The surname.
+ * @param givenName A sequence of directory strings making up the givenName
+ */
+ public NameOrPseudonym(DirectoryString surname, ASN1Sequence givenName)
+ {
+ this.surname = surname;
+ this.givenName = givenName;
+ }
+
+ public DirectoryString getPseudonym()
+ {
+ return pseudonym;
+ }
+
+ public DirectoryString getSurname()
+ {
+ return surname;
+ }
+
+ public DirectoryString[] getGivenName()
+ {
+ DirectoryString[] items = new DirectoryString[givenName.size()];
+ int count = 0;
+ for (Enumeration e = givenName.getObjects(); e.hasMoreElements();)
+ {
+ items[count++] = DirectoryString.getInstance(e.nextElement());
+ }
+ return items;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p>
+ * Returns:
+ * <pre>
+ * NameOrPseudonym ::= CHOICE {
+ * surAndGivenName SEQUENCE {
+ * surName DirectoryString,
+ * givenName SEQUENCE OF DirectoryString
+ * },
+ * pseudonym DirectoryString
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ if (pseudonym != null)
+ {
+ return pseudonym.toASN1Primitive();
+ }
+ else
+ {
+ ASN1EncodableVector vec1 = new ASN1EncodableVector();
+ vec1.add(surname);
+ vec1.add(givenName);
+ return new DERSequence(vec1);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java b/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java
new file mode 100644
index 00000000..dec8ded6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/sigi/PersonalData.java
@@ -0,0 +1,213 @@
+package org.spongycastle.asn1.x509.sigi;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Object;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.x500.DirectoryString;
+
+/**
+ * Contains personal data for the otherName field in the subjectAltNames
+ * extension.
+ *
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @see org.spongycastle.asn1.x509.sigi.NameOrPseudonym
+ * @see org.spongycastle.asn1.x509.sigi.SigIObjectIdentifiers
+ */
+public class PersonalData
+ extends ASN1Object
+{
+ private NameOrPseudonym nameOrPseudonym;
+ private BigInteger nameDistinguisher;
+ private ASN1GeneralizedTime dateOfBirth;
+ private DirectoryString placeOfBirth;
+ private String gender;
+ private DirectoryString postalAddress;
+
+ public static PersonalData getInstance(Object obj)
+ {
+ if (obj == null || obj instanceof PersonalData)
+ {
+ return (PersonalData)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new PersonalData((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from ASN1Sequence.
+ * <p/>
+ * The sequence is of type NameOrPseudonym:
+ * <p/>
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @param seq The ASN.1 sequence.
+ */
+ private PersonalData(ASN1Sequence seq)
+ {
+ if (seq.size() < 1)
+ {
+ throw new IllegalArgumentException("Bad sequence size: "
+ + seq.size());
+ }
+
+ Enumeration e = seq.getObjects();
+
+ nameOrPseudonym = NameOrPseudonym.getInstance(e.nextElement());
+
+ while (e.hasMoreElements())
+ {
+ ASN1TaggedObject o = ASN1TaggedObject.getInstance(e.nextElement());
+ int tag = o.getTagNo();
+ switch (tag)
+ {
+ case 0:
+ nameDistinguisher = ASN1Integer.getInstance(o, false).getValue();
+ break;
+ case 1:
+ dateOfBirth = ASN1GeneralizedTime.getInstance(o, false);
+ break;
+ case 2:
+ placeOfBirth = DirectoryString.getInstance(o, true);
+ break;
+ case 3:
+ gender = DERPrintableString.getInstance(o, false).getString();
+ break;
+ case 4:
+ postalAddress = DirectoryString.getInstance(o, true);
+ break;
+ default:
+ throw new IllegalArgumentException("Bad tag number: " + o.getTagNo());
+ }
+ }
+ }
+
+ /**
+ * Constructor from a given details.
+ *
+ * @param nameOrPseudonym Name or pseudonym.
+ * @param nameDistinguisher Name distinguisher.
+ * @param dateOfBirth Date of birth.
+ * @param placeOfBirth Place of birth.
+ * @param gender Gender.
+ * @param postalAddress Postal Address.
+ */
+ public PersonalData(NameOrPseudonym nameOrPseudonym,
+ BigInteger nameDistinguisher, ASN1GeneralizedTime dateOfBirth,
+ DirectoryString placeOfBirth, String gender, DirectoryString postalAddress)
+ {
+ this.nameOrPseudonym = nameOrPseudonym;
+ this.dateOfBirth = dateOfBirth;
+ this.gender = gender;
+ this.nameDistinguisher = nameDistinguisher;
+ this.postalAddress = postalAddress;
+ this.placeOfBirth = placeOfBirth;
+ }
+
+ public NameOrPseudonym getNameOrPseudonym()
+ {
+ return nameOrPseudonym;
+ }
+
+ public BigInteger getNameDistinguisher()
+ {
+ return nameDistinguisher;
+ }
+
+ public ASN1GeneralizedTime getDateOfBirth()
+ {
+ return dateOfBirth;
+ }
+
+ public DirectoryString getPlaceOfBirth()
+ {
+ return placeOfBirth;
+ }
+
+ public String getGender()
+ {
+ return gender;
+ }
+
+ public DirectoryString getPostalAddress()
+ {
+ return postalAddress;
+ }
+
+ /**
+ * Produce an object suitable for an ASN1OutputStream.
+ * <p>
+ * Returns:
+ * <pre>
+ * PersonalData ::= SEQUENCE {
+ * nameOrPseudonym NameOrPseudonym,
+ * nameDistinguisher [0] INTEGER OPTIONAL,
+ * dateOfBirth [1] GeneralizedTime OPTIONAL,
+ * placeOfBirth [2] DirectoryString OPTIONAL,
+ * gender [3] PrintableString OPTIONAL,
+ * postalAddress [4] DirectoryString OPTIONAL
+ * }
+ * </pre>
+ *
+ * @return a DERObject
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ vec.add(nameOrPseudonym);
+ if (nameDistinguisher != null)
+ {
+ vec.add(new DERTaggedObject(false, 0, new ASN1Integer(nameDistinguisher)));
+ }
+ if (dateOfBirth != null)
+ {
+ vec.add(new DERTaggedObject(false, 1, dateOfBirth));
+ }
+ if (placeOfBirth != null)
+ {
+ vec.add(new DERTaggedObject(true, 2, placeOfBirth));
+ }
+ if (gender != null)
+ {
+ vec.add(new DERTaggedObject(false, 3, new DERPrintableString(gender, true)));
+ }
+ if (postalAddress != null)
+ {
+ vec.add(new DERTaggedObject(true, 4, postalAddress));
+ }
+ return new DERSequence(vec);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
new file mode 100644
index 00000000..5d9b25b3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
@@ -0,0 +1,60 @@
+package org.spongycastle.asn1.x509.sigi;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Object Identifiers of SigI specifciation (German Signature Law
+ * Interoperability specification).
+ */
+public interface SigIObjectIdentifiers
+{
+ /**
+ * OID: 1.3.36.8
+ */
+ public final static ASN1ObjectIdentifier id_sigi = new ASN1ObjectIdentifier("1.3.36.8");
+
+ /**
+ * Key purpose IDs for German SigI (Signature Interoperability
+ * Specification)
+ * <p>
+ * OID: 1.3.36.8.2
+ */
+ public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier("1.3.36.8.2");
+
+ /**
+ * Certificate policy IDs for German SigI (Signature Interoperability
+ * Specification)
+ * <p>
+ * OID: 1.3.36.8.1
+ */
+ public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier("1.3.36.8.1");
+
+ /**
+ * Other Name IDs for German SigI (Signature Interoperability Specification)
+ * <p>
+ * OID: 1.3.36.8.4
+ */
+ public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier("1.3.36.8.4");
+
+ /**
+ * To be used for for the generation of directory service certificates.
+ * <p>
+ * OID: 1.3.36.8.2.1
+ */
+ public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier("1.3.36.8.2.1");
+
+ /**
+ * ID for PersonalData
+ * <p>
+ * OID: 1.3.36.8.4.1
+ */
+ public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier("1.3.36.8.4.1");
+
+ /**
+ * Certificate is conformant to german signature law.
+ * <p>
+ * OID: 1.3.36.8.1.1
+ */
+ public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier("1.3.36.8.1.1");
+
+}