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/x500')
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java72
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java125
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/RDN.java119
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/X500Name.java326
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java87
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java79
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/AbstractX500NameStyle.java192
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java36
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java349
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java572
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java248
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java90
12 files changed, 2295 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java b/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java
new file mode 100644
index 00000000..57fa4c25
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/AttributeTypeAndValue.java
@@ -0,0 +1,72 @@
+package org.spongycastle.asn1.x500;
+
+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;
+
+public class AttributeTypeAndValue
+ extends ASN1Object
+{
+ private ASN1ObjectIdentifier type;
+ private ASN1Encodable value;
+
+ private AttributeTypeAndValue(ASN1Sequence seq)
+ {
+ type = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+ value = (ASN1Encodable)seq.getObjectAt(1);
+ }
+
+ public static AttributeTypeAndValue getInstance(Object o)
+ {
+ if (o instanceof AttributeTypeAndValue)
+ {
+ return (AttributeTypeAndValue)o;
+ }
+ else if (o != null)
+ {
+ return new AttributeTypeAndValue(ASN1Sequence.getInstance(o));
+ }
+
+ throw new IllegalArgumentException("null value in getInstance()");
+ }
+
+ public AttributeTypeAndValue(
+ ASN1ObjectIdentifier type,
+ ASN1Encodable value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ /**
+ * <pre>
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY DEFINED BY type }
+ * </pre>
+ * @return a basic ASN.1 object representation.
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(type);
+ v.add(value);
+
+ return new DERSequence(v);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java b/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java
new file mode 100644
index 00000000..8e61383b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/DirectoryString.java
@@ -0,0 +1,125 @@
+package org.spongycastle.asn1.x500;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+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.DERPrintableString;
+import org.spongycastle.asn1.DERT61String;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.DERUniversalString;
+
+public class DirectoryString
+ extends ASN1Object
+ implements ASN1Choice, ASN1String
+{
+ private ASN1String string;
+
+ public static DirectoryString getInstance(Object o)
+ {
+ if (o == null || o instanceof DirectoryString)
+ {
+ return (DirectoryString)o;
+ }
+
+ if (o instanceof DERT61String)
+ {
+ return new DirectoryString((DERT61String)o);
+ }
+
+ if (o instanceof DERPrintableString)
+ {
+ return new DirectoryString((DERPrintableString)o);
+ }
+
+ if (o instanceof DERUniversalString)
+ {
+ return new DirectoryString((DERUniversalString)o);
+ }
+
+ if (o instanceof DERUTF8String)
+ {
+ return new DirectoryString((DERUTF8String)o);
+ }
+
+ if (o instanceof DERBMPString)
+ {
+ return new DirectoryString((DERBMPString)o);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
+ }
+
+ public static DirectoryString getInstance(ASN1TaggedObject o, boolean explicit)
+ {
+ if (!explicit)
+ {
+ throw new IllegalArgumentException("choice item must be explicitly tagged");
+ }
+
+ return getInstance(o.getObject());
+ }
+
+ private DirectoryString(
+ DERT61String string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERPrintableString string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERUniversalString string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERUTF8String string)
+ {
+ this.string = string;
+ }
+
+ private DirectoryString(
+ DERBMPString string)
+ {
+ this.string = string;
+ }
+
+ public DirectoryString(String string)
+ {
+ this.string = new DERUTF8String(string);
+ }
+
+ public String getString()
+ {
+ return string.getString();
+ }
+
+ public String toString()
+ {
+ return string.getString();
+ }
+
+ /**
+ * <pre>
+ * DirectoryString ::= CHOICE {
+ * teletexString TeletexString (SIZE (1..MAX)),
+ * printableString PrintableString (SIZE (1..MAX)),
+ * universalString UniversalString (SIZE (1..MAX)),
+ * utf8String UTF8String (SIZE (1..MAX)),
+ * bmpString BMPString (SIZE (1..MAX)) }
+ * </pre>
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return ((ASN1Encodable)string).toASN1Primitive();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/RDN.java b/core/src/main/java/org/spongycastle/asn1/x500/RDN.java
new file mode 100644
index 00000000..5e70cfb3
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/RDN.java
@@ -0,0 +1,119 @@
+package org.spongycastle.asn1.x500;
+
+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.ASN1Set;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+
+public class RDN
+ extends ASN1Object
+{
+ private ASN1Set values;
+
+ private RDN(ASN1Set values)
+ {
+ this.values = values;
+ }
+
+ public static RDN getInstance(Object obj)
+ {
+ if (obj instanceof RDN)
+ {
+ return (RDN)obj;
+ }
+ else if (obj != null)
+ {
+ return new RDN(ASN1Set.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a single valued RDN.
+ *
+ * @param oid RDN type.
+ * @param value RDN value.
+ */
+ public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(oid);
+ v.add(value);
+
+ this.values = new DERSet(new DERSequence(v));
+ }
+
+ public RDN(AttributeTypeAndValue attrTAndV)
+ {
+ this.values = new DERSet(attrTAndV);
+ }
+
+ /**
+ * Create a multi-valued RDN.
+ *
+ * @param aAndVs attribute type/value pairs making up the RDN
+ */
+ public RDN(AttributeTypeAndValue[] aAndVs)
+ {
+ this.values = new DERSet(aAndVs);
+ }
+
+ public boolean isMultiValued()
+ {
+ return this.values.size() > 1;
+ }
+
+ /**
+ * Return the number of AttributeTypeAndValue objects in this RDN,
+ *
+ * @return size of RDN, greater than 1 if multi-valued.
+ */
+ public int size()
+ {
+ return this.values.size();
+ }
+
+ public AttributeTypeAndValue getFirst()
+ {
+ if (this.values.size() == 0)
+ {
+ return null;
+ }
+
+ return AttributeTypeAndValue.getInstance(this.values.getObjectAt(0));
+ }
+
+ public AttributeTypeAndValue[] getTypesAndValues()
+ {
+ AttributeTypeAndValue[] tmp = new AttributeTypeAndValue[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = AttributeTypeAndValue.getInstance(values.getObjectAt(i));
+ }
+
+ return tmp;
+ }
+
+ /**
+ * <pre>
+ * RelativeDistinguishedName ::=
+ * SET OF AttributeTypeAndValue
+
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ * </pre>
+ * @return this object as an ASN1Primitive type
+ */
+ public ASN1Primitive toASN1Primitive()
+ {
+ return values;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java b/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java
new file mode 100644
index 00000000..a5e01b31
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/X500Name.java
@@ -0,0 +1,326 @@
+package org.spongycastle.asn1.x500;
+
+import java.util.Enumeration;
+
+import org.spongycastle.asn1.ASN1Choice;
+import org.spongycastle.asn1.ASN1Encodable;
+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;
+import org.spongycastle.asn1.x500.style.BCStyle;
+
+/**
+ * <pre>
+ * Name ::= CHOICE {
+ * RDNSequence }
+ *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * value ANY }
+ * </pre>
+ */
+public class X500Name
+ extends ASN1Object
+ implements ASN1Choice
+{
+ private static X500NameStyle defaultStyle = BCStyle.INSTANCE;
+
+ private boolean isHashCodeCalculated;
+ private int hashCodeValue;
+
+ private X500NameStyle style;
+ private RDN[] rdns;
+
+ public X500Name(X500NameStyle style, X500Name name)
+ {
+ this.rdns = name.rdns;
+ this.style = style;
+ }
+
+ /**
+ * Return a X500Name based on the passed in tagged object.
+ *
+ * @param obj tag object holding name.
+ * @param explicit true if explicitly tagged false otherwise.
+ * @return the X500Name
+ */
+ public static X500Name getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ // must be true as choice item
+ return getInstance(ASN1Sequence.getInstance(obj, true));
+ }
+
+ public static X500Name getInstance(
+ Object obj)
+ {
+ if (obj instanceof X500Name)
+ {
+ return (X500Name)obj;
+ }
+ else if (obj != null)
+ {
+ return new X500Name(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ public static X500Name getInstance(
+ X500NameStyle style,
+ Object obj)
+ {
+ if (obj instanceof X500Name)
+ {
+ return getInstance(style, ((X500Name)obj).toASN1Primitive());
+ }
+ else if (obj != null)
+ {
+ return new X500Name(style, ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ *
+ * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+ */
+ private X500Name(
+ ASN1Sequence seq)
+ {
+ this(defaultStyle, seq);
+ }
+
+ private X500Name(
+ X500NameStyle style,
+ ASN1Sequence seq)
+ {
+ this.style = style;
+ this.rdns = new RDN[seq.size()];
+
+ int index = 0;
+
+ for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+ {
+ rdns[index++] = RDN.getInstance(e.nextElement());
+ }
+ }
+
+ public X500Name(
+ RDN[] rDNs)
+ {
+ this(defaultStyle, rDNs);
+ }
+
+ public X500Name(
+ X500NameStyle style,
+ RDN[] rDNs)
+ {
+ this.rdns = rDNs;
+ this.style = style;
+ }
+
+ public X500Name(
+ String dirName)
+ {
+ this(defaultStyle, dirName);
+ }
+
+ public X500Name(
+ X500NameStyle style,
+ String dirName)
+ {
+ this(style.fromString(dirName));
+
+ this.style = style;
+ }
+
+ /**
+ * return an array of RDNs in structure order.
+ *
+ * @return an array of RDN objects.
+ */
+ public RDN[] getRDNs()
+ {
+ RDN[] tmp = new RDN[this.rdns.length];
+
+ System.arraycopy(rdns, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ /**
+ * return an array of OIDs contained in the attribute type of each RDN in structure order.
+ *
+ * @return an array, possibly zero length, of ASN1ObjectIdentifiers objects.
+ */
+ public ASN1ObjectIdentifier[] getAttributeTypes()
+ {
+ int count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ count += rdn.size();
+ }
+
+ ASN1ObjectIdentifier[] res = new ASN1ObjectIdentifier[count];
+
+ count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
+ for (int j = 0; j != attr.length; j++)
+ {
+ res[count++] = attr[j].getType();
+ }
+ }
+ else if (rdn.size() != 0)
+ {
+ res[count++] = rdn.getFirst().getType();
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * return an array of RDNs containing the attribute type given by OID in structure order.
+ *
+ * @param attributeType the type OID we are looking for.
+ * @return an array, possibly zero length, of RDN objects.
+ */
+ public RDN[] getRDNs(ASN1ObjectIdentifier attributeType)
+ {
+ RDN[] res = new RDN[rdns.length];
+ int count = 0;
+
+ for (int i = 0; i != rdns.length; i++)
+ {
+ RDN rdn = rdns[i];
+
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
+ for (int j = 0; j != attr.length; j++)
+ {
+ if (attr[j].getType().equals(attributeType))
+ {
+ res[count++] = rdn;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (rdn.getFirst().getType().equals(attributeType))
+ {
+ res[count++] = rdn;
+ }
+ }
+ }
+
+ RDN[] tmp = new RDN[count];
+
+ System.arraycopy(res, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ public ASN1Primitive toASN1Primitive()
+ {
+ return new DERSequence(rdns);
+ }
+
+ public int hashCode()
+ {
+ if (isHashCodeCalculated)
+ {
+ return hashCodeValue;
+ }
+
+ isHashCodeCalculated = true;
+
+ hashCodeValue = style.calculateHashCode(this);
+
+ return hashCodeValue;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof X500Name || obj instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
+
+ if (this.toASN1Primitive().equals(derO))
+ {
+ return true;
+ }
+
+ try
+ {
+ return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public String toString()
+ {
+ return style.toString(this);
+ }
+
+ /**
+ * Set the default style for X500Name construction.
+ *
+ * @param style an X500NameStyle
+ */
+ public static void setDefaultStyle(X500NameStyle style)
+ {
+ if (style == null)
+ {
+ throw new NullPointerException("cannot set style to null");
+ }
+
+ defaultStyle = style;
+ }
+
+ /**
+ * Return the current default style.
+ *
+ * @return default style for X500Name construction.
+ */
+ public static X500NameStyle getDefaultStyle()
+ {
+ return defaultStyle;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java b/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java
new file mode 100644
index 00000000..6640eff9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/X500NameBuilder.java
@@ -0,0 +1,87 @@
+package org.spongycastle.asn1.x500;
+
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x500.style.BCStyle;
+
+public class X500NameBuilder
+{
+ private X500NameStyle template;
+ private Vector rdns = new Vector();
+
+ public X500NameBuilder()
+ {
+ this(BCStyle.INSTANCE);
+ }
+
+ public X500NameBuilder(X500NameStyle template)
+ {
+ this.template = template;
+ }
+
+ public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, String value)
+ {
+ this.addRDN(oid, template.stringToValue(oid, value));
+
+ return this;
+ }
+
+ public X500NameBuilder addRDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
+ {
+ rdns.addElement(new RDN(oid, value));
+
+ return this;
+ }
+
+ public X500NameBuilder addRDN(AttributeTypeAndValue attrTAndV)
+ {
+ rdns.addElement(new RDN(attrTAndV));
+
+ return this;
+ }
+
+ public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, String[] values)
+ {
+ ASN1Encodable[] vals = new ASN1Encodable[values.length];
+
+ for (int i = 0; i != vals.length; i++)
+ {
+ vals[i] = template.stringToValue(oids[i], values[i]);
+ }
+
+ return addMultiValuedRDN(oids, vals);
+ }
+
+ public X500NameBuilder addMultiValuedRDN(ASN1ObjectIdentifier[] oids, ASN1Encodable[] values)
+ {
+ AttributeTypeAndValue[] avs = new AttributeTypeAndValue[oids.length];
+
+ for (int i = 0; i != oids.length; i++)
+ {
+ avs[i] = new AttributeTypeAndValue(oids[i], values[i]);
+ }
+
+ return addMultiValuedRDN(avs);
+ }
+
+ public X500NameBuilder addMultiValuedRDN(AttributeTypeAndValue[] attrTAndVs)
+ {
+ rdns.addElement(new RDN(attrTAndVs));
+
+ return this;
+ }
+
+ public X500Name build()
+ {
+ RDN[] vals = new RDN[rdns.size()];
+
+ for (int i = 0; i != vals.length; i++)
+ {
+ vals[i] = (RDN)rdns.elementAt(i);
+ }
+
+ return new X500Name(template, vals);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java b/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java
new file mode 100644
index 00000000..8c716170
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/X500NameStyle.java
@@ -0,0 +1,79 @@
+package org.spongycastle.asn1.x500;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * 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 X500Name class
+ * will produce UTF8Strings in line with the current recommendations (RFC 3280).
+ * <p>
+ */
+public interface X500NameStyle
+{
+ /**
+ * 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.
+ */
+ ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value);
+
+ /**
+ * Return the OID associated with the passed in name.
+ *
+ * @param attrName the string to match.
+ * @return an OID
+ */
+ ASN1ObjectIdentifier attrNameToOID(String attrName);
+
+ /**
+ * Return an array of RDN generated from the passed in String.
+ * @param dirName the String representation.
+ * @return an array of corresponding RDNs.
+ */
+ RDN[] fromString(String dirName);
+
+ /**
+ * Return true if the two names are equal.
+ *
+ * @param name1 first name for comparison.
+ * @param name2 second name for comparison.
+ * @return true if name1 = name 2, false otherwise.
+ */
+ boolean areEqual(X500Name name1, X500Name name2);
+
+ /**
+ * Calculate a hashCode for the passed in name.
+ *
+ * @param name the name the hashCode is required for.
+ * @return the calculated hashCode.
+ */
+ int calculateHashCode(X500Name name);
+
+ /**
+ * Convert the passed in X500Name to a String.
+ * @param name the name to convert.
+ * @return a String representation.
+ */
+ String toString(X500Name name);
+
+ /**
+ * Return the display name for toString() associated with the OID.
+ *
+ * @param oid the OID of interest.
+ * @return the name displayed in toString(), null if no mapping provided.
+ */
+ String oidToDisplayName(ASN1ObjectIdentifier oid);
+
+ /**
+ * Return the acceptable names in a String DN that map to OID.
+ *
+ * @param oid the OID of interest.
+ * @return an array of String aliases for the OID, zero length if there are none.
+ */
+ String[] oidToAttrNames(ASN1ObjectIdentifier oid);
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/AbstractX500NameStyle.java b/core/src/main/java/org/spongycastle/asn1/x500/style/AbstractX500NameStyle.java
new file mode 100644
index 00000000..dfab0377
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/AbstractX500NameStyle.java
@@ -0,0 +1,192 @@
+package org.spongycastle.asn1.x500.style;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.x500.AttributeTypeAndValue;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+
+/**
+ * This class provides some default behavior and common implementation for a
+ * X500NameStyle. It should be easily extendable to support implementing the
+ * desired X500NameStyle.
+ */
+public abstract class AbstractX500NameStyle
+ implements X500NameStyle
+{
+
+ /**
+ * Tool function to shallow copy a Hashtable.
+ *
+ * @param paramsMap table to copy
+ * @return the copy of the table
+ */
+ public static Hashtable copyHashTable(Hashtable paramsMap)
+ {
+ Hashtable newTable = new Hashtable();
+
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ newTable.put(key, paramsMap.get(key));
+ }
+
+ return newTable;
+ }
+
+ private int calcHashCode(ASN1Encodable enc)
+ {
+ String value = IETFUtils.valueToString(enc);
+ value = IETFUtils.canonicalize(value);
+ return value.hashCode();
+ }
+
+ public int calculateHashCode(X500Name name)
+ {
+ int hashCodeValue = 0;
+ RDN[] rdns = name.getRDNs();
+
+ // this needs to be order independent, like equals
+ for (int i = 0; i != rdns.length; i++)
+ {
+ if (rdns[i].isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ hashCodeValue ^= atv[j].getType().hashCode();
+ hashCodeValue ^= calcHashCode(atv[j].getValue());
+ }
+ }
+ else
+ {
+ hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
+ hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
+ }
+ }
+
+ return hashCodeValue;
+ }
+
+
+ /**
+ * For all string values starting with '#' is assumed, that these are
+ * already valid ASN.1 objects encoded in hex.
+ * <p>
+ * All other string values are send to
+ * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}.
+ * </p>
+ * Subclasses should overwrite
+ * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}
+ * to change the encoding of specific types.
+ *
+ * @param oid the DN name of the value.
+ * @param value the String representation of the value.
+ */
+ public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
+ {
+ if (value.length() != 0 && value.charAt(0) == '#')
+ {
+ try
+ {
+ return IETFUtils.valueFromHexString(value, 1);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("can't recode value for oid " + oid.getId());
+ }
+ }
+
+ if (value.length() != 0 && value.charAt(0) == '\\')
+ {
+ value = value.substring(1);
+ }
+
+ return encodeStringValue(oid, value);
+ }
+
+ /**
+ * Encoded every value into a UTF8String.
+ * <p>
+ * Subclasses should overwrite
+ * this method to change the encoding of specific types.
+ * </p>
+ *
+ * @param oid the DN oid of the value
+ * @param value the String representation of the value
+ * @return a the value encoded into a ASN.1 object. Never returns <code>null</code>.
+ */
+ protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+ {
+ return new DERUTF8String(value);
+ }
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ boolean reverse = false;
+
+ if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+ {
+ reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!foundMatch(reverse, rdns1[i], rdns2))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
+ {
+ if (reverse)
+ {
+ for (int i = possRDNs.length - 1; i >= 0; i--)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != possRDNs.length; i++)
+ {
+ if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
+ {
+ possRDNs[i] = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
+ {
+ return IETFUtils.rDNAreEqual(rdn1, rdn2);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java b/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java
new file mode 100644
index 00000000..23609e72
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/BCStrictStyle.java
@@ -0,0 +1,36 @@
+package org.spongycastle.asn1.x500.style;
+
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+
+/**
+ * Variation of BCStyle that insists on strict ordering for equality
+ * and hashCode comparisons
+ */
+public class BCStrictStyle
+ extends BCStyle
+{
+ public static final X500NameStyle INSTANCE = new BCStrictStyle();
+
+ public boolean areEqual(X500Name name1, X500Name name2)
+ {
+ RDN[] rdns1 = name1.getRDNs();
+ RDN[] rdns2 = name2.getRDNs();
+
+ if (rdns1.length != rdns2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != rdns1.length; i++)
+ {
+ if (!rdnAreEqual(rdns1[i], rdns2[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java b/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java
new file mode 100644
index 00000000..cb92a0bd
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/BCStyle.java
@@ -0,0 +1,349 @@
+package org.spongycastle.asn1.x500.style;
+
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+import org.spongycastle.asn1.x509.X509ObjectIdentifiers;
+
+public class BCStyle
+ extends AbstractX500NameStyle
+{
+ /**
+ * country code - StringType(SIZE(2))
+ */
+ public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ */
+ public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ */
+ public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ */
+ 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.
+ */
+ 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");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ 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");
+
+ 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);
+ }
+
+ /**
+ * Singleton instance.
+ */
+ public static final X500NameStyle INSTANCE = new BCStyle();
+
+ protected final Hashtable defaultLookUp;
+ protected final Hashtable defaultSymbols;
+
+ protected BCStyle()
+ {
+ defaultSymbols = copyHashTable(DefaultSymbols);
+ defaultLookUp = copyHashTable(DefaultLookUp);
+ }
+
+ protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
+ String value) {
+ if (oid.equals(EmailAddress) || oid.equals(DC))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(DATE_OF_BIRTH)) // accept time string as well as # (for compatibility)
+ {
+ return new ASN1GeneralizedTime(value);
+ }
+ else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+ || oid.equals(TELEPHONE_NUMBER))
+ {
+ return new DERPrintableString(value);
+ }
+
+ return super.encodeStringValue(oid, value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, defaultLookUp);
+ }
+
+ public RDN[] fromString(String dirName)
+ {
+ return IETFUtils.rDNsFromString(dirName, this);
+ }
+
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = 0; i < rdns.length; i++)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
+ }
+
+ return buf.toString();
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java b/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java
new file mode 100644
index 00000000..14961e4c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/IETFUtils.java
@@ -0,0 +1,572 @@
+package org.spongycastle.asn1.x500.style;
+
+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.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.DERUniversalString;
+import org.spongycastle.asn1.x500.AttributeTypeAndValue;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500NameBuilder;
+import org.spongycastle.asn1.x500.X500NameStyle;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.encoders.Hex;
+
+public class IETFUtils
+{
+ private static 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;
+ char hex1 = 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;
+ }
+ if (escaped && isHexDigit(c))
+ {
+ if (hex1 != 0)
+ {
+ buf.append((char)(convertHex(hex1) * 16 + convertHex(c)));
+ escaped = false;
+ hex1 = 0;
+ continue;
+ }
+ hex1 = c;
+ 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();
+ }
+
+ private static boolean isHexDigit(char c)
+ {
+ return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+ }
+
+ private static int convertHex(char c)
+ {
+ if ('0' <= c && c <= '9')
+ {
+ return c - '0';
+ }
+ if ('a' <= c && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ return c - 'A' + 10;
+ }
+
+ public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
+ {
+ X500NameTokenizer nTok = new X500NameTokenizer(name);
+ X500NameBuilder builder = new X500NameBuilder(x500Style);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+
+ if (token.indexOf('+') > 0)
+ {
+ X500NameTokenizer pTok = new X500NameTokenizer(token, '+');
+ X500NameTokenizer vTok = new X500NameTokenizer(pTok.nextToken(), '=');
+
+ String attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ String value = vTok.nextToken();
+ ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
+
+ if (pTok.hasMoreTokens())
+ {
+ Vector oids = new Vector();
+ Vector values = new Vector();
+
+ oids.addElement(oid);
+ values.addElement(unescape(value));
+
+ while (pTok.hasMoreTokens())
+ {
+ vTok = new X500NameTokenizer(pTok.nextToken(), '=');
+
+ attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ value = vTok.nextToken();
+ oid = x500Style.attrNameToOID(attr.trim());
+
+
+ oids.addElement(oid);
+ values.addElement(unescape(value));
+ }
+
+ builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
+ }
+ else
+ {
+ builder.addRDN(oid, unescape(value));
+ }
+ }
+ else
+ {
+ X500NameTokenizer vTok = new X500NameTokenizer(token, '=');
+
+ String attr = vTok.nextToken();
+
+ if (!vTok.hasMoreTokens())
+ {
+ throw new IllegalArgumentException("badly formatted directory string");
+ }
+
+ String value = vTok.nextToken();
+ ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
+
+ builder.addRDN(oid, unescape(value));
+ }
+ }
+
+ return builder.build().getRDNs();
+ }
+
+ private static String[] toValueArray(Vector values)
+ {
+ String[] tmp = new String[values.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (String)values.elementAt(i);
+ }
+
+ return tmp;
+ }
+
+ private static ASN1ObjectIdentifier[] toOIDArray(Vector oids)
+ {
+ ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i);
+ }
+
+ return tmp;
+ }
+
+ public static String[] findAttrNamesForOID(
+ ASN1ObjectIdentifier oid,
+ Hashtable lookup)
+ {
+ int count = 0;
+ for (Enumeration en = lookup.elements(); en.hasMoreElements();)
+ {
+ if (oid.equals(en.nextElement()))
+ {
+ count++;
+ }
+ }
+
+ String[] aliases = new String[count];
+ count = 0;
+
+ for (Enumeration en = lookup.keys(); en.hasMoreElements();)
+ {
+ String key = (String)en.nextElement();
+ if (oid.equals(lookup.get(key)))
+ {
+ aliases[count++] = key;
+ }
+ }
+
+ return aliases;
+ }
+
+ public static ASN1ObjectIdentifier decodeAttrName(
+ String name,
+ Hashtable lookUp)
+ {
+ 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;
+ }
+
+ public static ASN1Encodable valueFromHexString(
+ String str,
+ int off)
+ throws IOException
+ {
+ 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);
+
+ data[index] = (byte)((convertHex(left) << 4) | convertHex(right));
+ }
+
+ return ASN1Primitive.fromByteArray(data);
+ }
+
+ public static void appendRDN(
+ StringBuffer buf,
+ RDN rdn,
+ Hashtable oidSymbols)
+ {
+ if (rdn.isMultiValued())
+ {
+ AttributeTypeAndValue[] atv = rdn.getTypesAndValues();
+ boolean firstAtv = true;
+
+ for (int j = 0; j != atv.length; j++)
+ {
+ if (firstAtv)
+ {
+ firstAtv = false;
+ }
+ else
+ {
+ buf.append('+');
+ }
+
+ IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols);
+ }
+ }
+ else
+ {
+ IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols);
+ }
+ }
+
+ public static void appendTypeAndValue(
+ StringBuffer buf,
+ AttributeTypeAndValue typeAndValue,
+ Hashtable oidSymbols)
+ {
+ String sym = (String)oidSymbols.get(typeAndValue.getType());
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(typeAndValue.getType().getId());
+ }
+
+ buf.append('=');
+
+ buf.append(valueToString(typeAndValue.getValue()));
+ }
+
+ public static String valueToString(ASN1Encodable value)
+ {
+ StringBuffer vBuf = new StringBuffer();
+
+ if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+ {
+ String v = ((ASN1String)value).getString();
+ if (v.length() > 0 && v.charAt(0) == '#')
+ {
+ vBuf.append("\\" + v);
+ }
+ else
+ {
+ vBuf.append(v);
+ }
+ }
+ else
+ {
+ try
+ {
+ vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Other value has no encoded form");
+ }
+ }
+
+ int end = vBuf.length();
+ int index = 0;
+
+ if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#')
+ {
+ index += 2;
+ }
+
+ while (index != end)
+ {
+ if ((vBuf.charAt(index) == ',')
+ || (vBuf.charAt(index) == '"')
+ || (vBuf.charAt(index) == '\\')
+ || (vBuf.charAt(index) == '+')
+ || (vBuf.charAt(index) == '=')
+ || (vBuf.charAt(index) == '<')
+ || (vBuf.charAt(index) == '>')
+ || (vBuf.charAt(index) == ';'))
+ {
+ vBuf.insert(index, "\\");
+ index++;
+ end++;
+ }
+
+ index++;
+ }
+
+ int start = 0;
+ if (vBuf.length() > 0)
+ {
+ while (vBuf.length() > start && vBuf.charAt(start) == ' ')
+ {
+ vBuf.insert(start, "\\");
+ start += 2;
+ }
+ }
+
+ int endBuf = vBuf.length() - 1;
+
+ while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
+ {
+ vBuf.insert(endBuf, '\\');
+ endBuf--;
+ }
+
+ return vBuf.toString();
+ }
+
+ private static 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 static 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());
+ }
+ }
+
+ value = stripInternalSpaces(value);
+
+ return value;
+ }
+
+ private static ASN1Primitive decodeObject(String oValue)
+ {
+ try
+ {
+ return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unknown encoding in name: " + e);
+ }
+ }
+
+ public static 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();
+ }
+
+ public static boolean rDNAreEqual(RDN rdn1, RDN rdn2)
+ {
+ if (rdn1.isMultiValued())
+ {
+ if (rdn2.isMultiValued())
+ {
+ AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues();
+ AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues();
+
+ if (atvs1.length != atvs2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != atvs1.length; i++)
+ {
+ if (!atvAreEqual(atvs1[i], atvs2[i]))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!rdn2.isMultiValued())
+ {
+ return atvAreEqual(rdn1.getFirst(), rdn2.getFirst());
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2)
+ {
+ if (atv1 == atv2)
+ {
+ return true;
+ }
+
+ if (atv1 == null)
+ {
+ return false;
+ }
+
+ if (atv2 == null)
+ {
+ return false;
+ }
+
+ ASN1ObjectIdentifier o1 = atv1.getType();
+ ASN1ObjectIdentifier o2 = atv2.getType();
+
+ if (!o1.equals(o2))
+ {
+ return false;
+ }
+
+ String v1 = IETFUtils.canonicalize(IETFUtils.valueToString(atv1.getValue()));
+ String v2 = IETFUtils.canonicalize(IETFUtils.valueToString(atv2.getValue()));
+
+ if (!v1.equals(v2))
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java b/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java
new file mode 100644
index 00000000..83825e6c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/RFC4519Style.java
@@ -0,0 +1,248 @@
+package org.spongycastle.asn1.x500.style;
+
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERPrintableString;
+import org.spongycastle.asn1.x500.RDN;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+
+public class RFC4519Style
+ extends AbstractX500NameStyle
+{
+ public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15");
+ public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6");
+ public static final ASN1ObjectIdentifier cn = new ASN1ObjectIdentifier("2.5.4.3");
+ public static final ASN1ObjectIdentifier dc = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
+ public static final ASN1ObjectIdentifier description = new ASN1ObjectIdentifier("2.5.4.13");
+ public static final ASN1ObjectIdentifier destinationIndicator = new ASN1ObjectIdentifier("2.5.4.27");
+ public static final ASN1ObjectIdentifier distinguishedName = new ASN1ObjectIdentifier("2.5.4.49");
+ public static final ASN1ObjectIdentifier dnQualifier = new ASN1ObjectIdentifier("2.5.4.46");
+ public static final ASN1ObjectIdentifier enhancedSearchGuide = new ASN1ObjectIdentifier("2.5.4.47");
+ public static final ASN1ObjectIdentifier facsimileTelephoneNumber = new ASN1ObjectIdentifier("2.5.4.23");
+ public static final ASN1ObjectIdentifier generationQualifier = new ASN1ObjectIdentifier("2.5.4.44");
+ public static final ASN1ObjectIdentifier givenName = new ASN1ObjectIdentifier("2.5.4.42");
+ public static final ASN1ObjectIdentifier houseIdentifier = new ASN1ObjectIdentifier("2.5.4.51");
+ public static final ASN1ObjectIdentifier initials = new ASN1ObjectIdentifier("2.5.4.43");
+ public static final ASN1ObjectIdentifier internationalISDNNumber = new ASN1ObjectIdentifier("2.5.4.25");
+ public static final ASN1ObjectIdentifier l = new ASN1ObjectIdentifier("2.5.4.7");
+ public static final ASN1ObjectIdentifier member = new ASN1ObjectIdentifier("2.5.4.31");
+ public static final ASN1ObjectIdentifier name = new ASN1ObjectIdentifier("2.5.4.41");
+ public static final ASN1ObjectIdentifier o = new ASN1ObjectIdentifier("2.5.4.10");
+ public static final ASN1ObjectIdentifier ou = new ASN1ObjectIdentifier("2.5.4.11");
+ public static final ASN1ObjectIdentifier owner = new ASN1ObjectIdentifier("2.5.4.32");
+ public static final ASN1ObjectIdentifier physicalDeliveryOfficeName = new ASN1ObjectIdentifier("2.5.4.19");
+ public static final ASN1ObjectIdentifier postalAddress = new ASN1ObjectIdentifier("2.5.4.16");
+ public static final ASN1ObjectIdentifier postalCode = new ASN1ObjectIdentifier("2.5.4.17");
+ public static final ASN1ObjectIdentifier postOfficeBox = new ASN1ObjectIdentifier("2.5.4.18");
+ public static final ASN1ObjectIdentifier preferredDeliveryMethod = new ASN1ObjectIdentifier("2.5.4.28");
+ public static final ASN1ObjectIdentifier registeredAddress = new ASN1ObjectIdentifier("2.5.4.26");
+ public static final ASN1ObjectIdentifier roleOccupant = new ASN1ObjectIdentifier("2.5.4.33");
+ public static final ASN1ObjectIdentifier searchGuide = new ASN1ObjectIdentifier("2.5.4.14");
+ public static final ASN1ObjectIdentifier seeAlso = new ASN1ObjectIdentifier("2.5.4.34");
+ public static final ASN1ObjectIdentifier serialNumber = new ASN1ObjectIdentifier("2.5.4.5");
+ public static final ASN1ObjectIdentifier sn = new ASN1ObjectIdentifier("2.5.4.4");
+ public static final ASN1ObjectIdentifier st = new ASN1ObjectIdentifier("2.5.4.8");
+ public static final ASN1ObjectIdentifier street = new ASN1ObjectIdentifier("2.5.4.9");
+ public static final ASN1ObjectIdentifier telephoneNumber = new ASN1ObjectIdentifier("2.5.4.20");
+ public static final ASN1ObjectIdentifier teletexTerminalIdentifier = new ASN1ObjectIdentifier("2.5.4.22");
+ public static final ASN1ObjectIdentifier telexNumber = new ASN1ObjectIdentifier("2.5.4.21");
+ public static final ASN1ObjectIdentifier title = new ASN1ObjectIdentifier("2.5.4.12");
+ public static final ASN1ObjectIdentifier uid = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
+ public static final ASN1ObjectIdentifier uniqueMember = new ASN1ObjectIdentifier("2.5.4.50");
+ public static final ASN1ObjectIdentifier userPassword = new ASN1ObjectIdentifier("2.5.4.35");
+ public static final ASN1ObjectIdentifier x121Address = new ASN1ObjectIdentifier("2.5.4.24");
+ public static final ASN1ObjectIdentifier x500UniqueIdentifier = new ASN1ObjectIdentifier("2.5.4.45");
+
+ /**
+ * default look up table translating OID values into their common symbols following
+ * the convention in RFC 2253 with a few extras
+ */
+ private static final Hashtable DefaultSymbols = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ private static final Hashtable DefaultLookUp = new Hashtable();
+
+ static
+ {
+ DefaultSymbols.put(businessCategory, "businessCategory");
+ DefaultSymbols.put(c, "c");
+ DefaultSymbols.put(cn, "cn");
+ DefaultSymbols.put(dc, "dc");
+ DefaultSymbols.put(description, "description");
+ DefaultSymbols.put(destinationIndicator, "destinationIndicator");
+ DefaultSymbols.put(distinguishedName, "distinguishedName");
+ DefaultSymbols.put(dnQualifier, "dnQualifier");
+ DefaultSymbols.put(enhancedSearchGuide, "enhancedSearchGuide");
+ DefaultSymbols.put(facsimileTelephoneNumber, "facsimileTelephoneNumber");
+ DefaultSymbols.put(generationQualifier, "generationQualifier");
+ DefaultSymbols.put(givenName, "givenName");
+ DefaultSymbols.put(houseIdentifier, "houseIdentifier");
+ DefaultSymbols.put(initials, "initials");
+ DefaultSymbols.put(internationalISDNNumber, "internationalISDNNumber");
+ DefaultSymbols.put(l, "l");
+ DefaultSymbols.put(member, "member");
+ DefaultSymbols.put(name, "name");
+ DefaultSymbols.put(o, "o");
+ DefaultSymbols.put(ou, "ou");
+ DefaultSymbols.put(owner, "owner");
+ DefaultSymbols.put(physicalDeliveryOfficeName, "physicalDeliveryOfficeName");
+ DefaultSymbols.put(postalAddress, "postalAddress");
+ DefaultSymbols.put(postalCode, "postalCode");
+ DefaultSymbols.put(postOfficeBox, "postOfficeBox");
+ DefaultSymbols.put(preferredDeliveryMethod, "preferredDeliveryMethod");
+ DefaultSymbols.put(registeredAddress, "registeredAddress");
+ DefaultSymbols.put(roleOccupant, "roleOccupant");
+ DefaultSymbols.put(searchGuide, "searchGuide");
+ DefaultSymbols.put(seeAlso, "seeAlso");
+ DefaultSymbols.put(serialNumber, "serialNumber");
+ DefaultSymbols.put(sn, "sn");
+ DefaultSymbols.put(st, "st");
+ DefaultSymbols.put(street, "street");
+ DefaultSymbols.put(telephoneNumber, "telephoneNumber");
+ DefaultSymbols.put(teletexTerminalIdentifier, "teletexTerminalIdentifier");
+ DefaultSymbols.put(telexNumber, "telexNumber");
+ DefaultSymbols.put(title, "title");
+ DefaultSymbols.put(uid, "uid");
+ DefaultSymbols.put(uniqueMember, "uniqueMember");
+ DefaultSymbols.put(userPassword, "userPassword");
+ DefaultSymbols.put(x121Address, "x121Address");
+ DefaultSymbols.put(x500UniqueIdentifier, "x500UniqueIdentifier");
+
+ DefaultLookUp.put("businesscategory", businessCategory);
+ DefaultLookUp.put("c", c);
+ DefaultLookUp.put("cn", cn);
+ DefaultLookUp.put("dc", dc);
+ DefaultLookUp.put("description", description);
+ DefaultLookUp.put("destinationindicator", destinationIndicator);
+ DefaultLookUp.put("distinguishedname", distinguishedName);
+ DefaultLookUp.put("dnqualifier", dnQualifier);
+ DefaultLookUp.put("enhancedsearchguide", enhancedSearchGuide);
+ DefaultLookUp.put("facsimiletelephonenumber", facsimileTelephoneNumber);
+ DefaultLookUp.put("generationqualifier", generationQualifier);
+ DefaultLookUp.put("givenname", givenName);
+ DefaultLookUp.put("houseidentifier", houseIdentifier);
+ DefaultLookUp.put("initials", initials);
+ DefaultLookUp.put("internationalisdnnumber", internationalISDNNumber);
+ DefaultLookUp.put("l", l);
+ DefaultLookUp.put("member", member);
+ DefaultLookUp.put("name", name);
+ DefaultLookUp.put("o", o);
+ DefaultLookUp.put("ou", ou);
+ DefaultLookUp.put("owner", owner);
+ DefaultLookUp.put("physicaldeliveryofficename", physicalDeliveryOfficeName);
+ DefaultLookUp.put("postaladdress", postalAddress);
+ DefaultLookUp.put("postalcode", postalCode);
+ DefaultLookUp.put("postofficebox", postOfficeBox);
+ DefaultLookUp.put("preferreddeliverymethod", preferredDeliveryMethod);
+ DefaultLookUp.put("registeredaddress", registeredAddress);
+ DefaultLookUp.put("roleoccupant", roleOccupant);
+ DefaultLookUp.put("searchguide", searchGuide);
+ DefaultLookUp.put("seealso", seeAlso);
+ DefaultLookUp.put("serialnumber", serialNumber);
+ DefaultLookUp.put("sn", sn);
+ DefaultLookUp.put("st", st);
+ DefaultLookUp.put("street", street);
+ DefaultLookUp.put("telephonenumber", telephoneNumber);
+ DefaultLookUp.put("teletexterminalidentifier", teletexTerminalIdentifier);
+ DefaultLookUp.put("telexnumber", telexNumber);
+ DefaultLookUp.put("title", title);
+ DefaultLookUp.put("uid", uid);
+ DefaultLookUp.put("uniquemember", uniqueMember);
+ DefaultLookUp.put("userpassword", userPassword);
+ DefaultLookUp.put("x121address", x121Address);
+ DefaultLookUp.put("x500uniqueidentifier", x500UniqueIdentifier);
+
+ // TODO: need to add correct matching for equality comparisons.
+ }
+
+ /**
+ * Singleton instance.
+ */
+ public static final X500NameStyle INSTANCE = new RFC4519Style();
+
+ protected final Hashtable defaultLookUp;
+ protected final Hashtable defaultSymbols;
+
+ protected RFC4519Style()
+ {
+ defaultSymbols = copyHashTable(DefaultSymbols);
+ defaultLookUp = copyHashTable(DefaultLookUp);
+ }
+
+ protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
+ String value) {
+ if (oid.equals(dc))
+ {
+ return new DERIA5String(value);
+ }
+ else if (oid.equals(c) || oid.equals(serialNumber) || oid.equals(dnQualifier)
+ || oid.equals(telephoneNumber))
+ {
+ return new DERPrintableString(value);
+ }
+
+ return super.encodeStringValue(oid, value);
+ }
+
+ public String oidToDisplayName(ASN1ObjectIdentifier oid)
+ {
+ return (String)DefaultSymbols.get(oid);
+ }
+
+ public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
+ {
+ return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
+ }
+
+ public ASN1ObjectIdentifier attrNameToOID(String attrName)
+ {
+ return IETFUtils.decodeAttrName(attrName, defaultLookUp);
+ }
+
+ // parse backwards
+ public RDN[] fromString(String dirName)
+ {
+ RDN[] tmp = IETFUtils.rDNsFromString(dirName, this);
+ RDN[] res = new RDN[tmp.length];
+
+ for (int i = 0; i != tmp.length; i++)
+ {
+ res[res.length - i - 1] = tmp[i];
+ }
+
+ return res;
+ }
+
+ // convert in reverse
+ public String toString(X500Name name)
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+
+ RDN[] rdns = name.getRDNs();
+
+ for (int i = rdns.length - 1; i >= 0; i--)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(',');
+ }
+
+ IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
+ }
+
+ return buf.toString();
+ }
+
+
+}
diff --git a/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java b/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java
new file mode 100644
index 00000000..65ce9865
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/asn1/x500/style/X500NameTokenizer.java
@@ -0,0 +1,90 @@
+package org.spongycastle.asn1.x500.style;
+
+/**
+ * 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.
+ */
+public class X500NameTokenizer
+{
+ private String value;
+ private int index;
+ private char separator;
+ private StringBuffer buf = new StringBuffer();
+
+ public X500NameTokenizer(
+ String oid)
+ {
+ this(oid, ',');
+ }
+
+ public X500NameTokenizer(
+ 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();
+ }
+}