diff --git a/Crypto/src/asn1/x509/GeneralName.cs b/Crypto/src/asn1/x509/GeneralName.cs
new file mode 100644
index 000000000..710ddc922
--- /dev/null
+++ b/Crypto/src/asn1/x509/GeneralName.cs
@@ -0,0 +1,418 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using NetUtils = Org.BouncyCastle.Utilities.Net;
+
+namespace Org.BouncyCastle.Asn1.X509
+{
+ /**
+ * 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 }
+ * </pre>
+ */
+ public class GeneralName
+ : Asn1Encodable, IAsn1Choice
+ {
+ public const int OtherName = 0;
+ public const int Rfc822Name = 1;
+ public const int DnsName = 2;
+ public const int X400Address = 3;
+ public const int DirectoryName = 4;
+ public const int EdiPartyName = 5;
+ public const int UniformResourceIdentifier = 6;
+ public const int IPAddress = 7;
+ public const int RegisteredID = 8;
+
+ internal readonly Asn1Encodable obj;
+ internal readonly int tag;
+
+ public GeneralName(
+ X509Name directoryName)
+ {
+ this.obj = directoryName;
+ 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(
+ Asn1Object name,
+ int tag)
+ {
+ this.obj = name;
+ this.tag = tag;
+ }
+
+ 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>
+ * <li>iPAddress</li>
+ * <li>directoryName</li>
+ * <li>dNSName</li>
+ * <li>uniformResourceIdentifier</li>
+ * <li>registeredID</li>
+ * </ul>
+ * For x400Address, otherName and ediPartyName there is no common string
+ * format defined.
+ * </p><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.
+ * </p>
+ *
+ * @param tag tag number
+ * @param name string representation of name
+ * @throws ArgumentException 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 DerObjectIdentifier(name);
+ }
+ else if (tag == DirectoryName)
+ {
+ this.obj = new X509Name(name);
+ }
+ else if (tag == IPAddress)
+ {
+ byte[] enc = toGeneralNameEncoding(name);
+ if (enc == null)
+ throw new ArgumentException("IP Address is invalid", "name");
+
+ this.obj = new DerOctetString(enc);
+ }
+ else
+ {
+ throw new ArgumentException("can't process string for tag: " + tag, "tag");
+ }
+ }
+
+ public static GeneralName GetInstance(
+ object obj)
+ {
+ if (obj == null || obj is GeneralName)
+ {
+ return (GeneralName) obj;
+ }
+
+ if (obj is Asn1TaggedObject)
+ {
+ Asn1TaggedObject tagObj = (Asn1TaggedObject) obj;
+ int tag = tagObj.TagNo;
+
+ 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 ArgumentException("unknown tag: " + tag);
+ case DirectoryName:
+ return new GeneralName(tag, X509Name.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, DerObjectIdentifier.GetInstance(tagObj, false));
+ }
+ }
+
+ if (obj is byte[])
+ {
+ try
+ {
+ return GetInstance(Asn1Object.FromByteArray((byte[])obj));
+ }
+ catch (IOException)
+ {
+ throw new ArgumentException("unable to parse encoded general name");
+ }
+ }
+
+ throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj");
+ }
+
+ public static GeneralName GetInstance(
+ Asn1TaggedObject tagObj,
+ bool explicitly)
+ {
+ return GetInstance(Asn1TaggedObject.GetInstance(tagObj, true));
+ }
+
+ public int TagNo
+ {
+ get { return tag; }
+ }
+
+ public Asn1Encodable Name
+ {
+ get { return obj; }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.Append(tag);
+ buf.Append(": ");
+
+ switch (tag)
+ {
+ case Rfc822Name:
+ case DnsName:
+ case UniformResourceIdentifier:
+ buf.Append(DerIA5String.GetInstance(obj).GetString());
+ break;
+ case DirectoryName:
+ buf.Append(X509Name.GetInstance(obj).ToString());
+ break;
+ default:
+ buf.Append(obj.ToString());
+ break;
+ }
+
+ return buf.ToString();
+ }
+
+ private byte[] toGeneralNameEncoding(
+ string ip)
+ {
+ if (NetUtils.IPAddress.IsValidIPv6WithNetmask(ip) || NetUtils.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 (NetUtils.IPAddress.IsValidIPv4WithNetmask(ip) || NetUtils.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 = Int32.Parse(mask);
+
+ for (int i = 0; i != maskVal; i++)
+ {
+ addr[(i / 8) + offset] |= (byte)(1 << (i % 8));
+ }
+ }
+
+ private void parseIPv4(string ip, byte[] addr, int offset)
+ {
+ foreach (string token in ip.Split('.', '/'))
+ {
+ addr[offset++] = (byte)Int32.Parse(token);
+ }
+ }
+
+ private int[] parseMask(string mask)
+ {
+ int[] res = new int[8];
+ int maskVal = Int32.Parse(mask);
+
+ for (int i = 0; i != maskVal; i++)
+ {
+ res[i / 16] |= 1 << (i % 16);
+ }
+ return res;
+ }
+
+ private void copyInts(int[] parsedIp, byte[] addr, int offSet)
+ {
+ for (int i = 0; i != parsedIp.Length; i++)
+ {
+ addr[(i * 2) + offSet] = (byte)(parsedIp[i] >> 8);
+ addr[(i * 2 + 1) + offSet] = (byte)parsedIp[i];
+ }
+ }
+
+ private int[] parseIPv6(string ip)
+ {
+ if (ip.StartsWith("::"))
+ {
+ ip = ip.Substring(1);
+ }
+ else if (ip.EndsWith("::"))
+ {
+ ip = ip.Substring(0, ip.Length - 1);
+ }
+
+ IEnumerator sEnum = ip.Split(':').GetEnumerator();
+
+ int index = 0;
+ int[] val = new int[8];
+
+ int doubleColon = -1;
+
+ while (sEnum.MoveNext())
+ {
+ string e = (string) sEnum.Current;
+
+ if (e.Length == 0)
+ {
+ doubleColon = index;
+ val[index++] = 0;
+ }
+ else
+ {
+ if (e.IndexOf('.') < 0)
+ {
+ val[index++] = Int32.Parse(e, NumberStyles.AllowHexSpecifier);
+ }
+ else
+ {
+ string[] tokens = e.Split('.');
+
+ val[index++] = (Int32.Parse(tokens[0]) << 8) | Int32.Parse(tokens[1]);
+ val[index++] = (Int32.Parse(tokens[2]) << 8) | Int32.Parse(tokens[3]);
+ }
+ }
+ }
+
+ if (index != val.Length)
+ {
+ Array.Copy(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 override Asn1Object ToAsn1Object()
+ {
+ // Explicitly tagged if DirectoryName
+ return new DerTaggedObject(tag == DirectoryName, tag, obj);
+ }
+ }
+}
|