#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) #pragma warning disable using System; using System.Collections; using System.Globalization; using System.IO; using System.Text; using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; using NetUtils = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Net; namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509 { /** * The GeneralName object. *
* 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 }
*
*/
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.
* * This constructor can handle: *
* 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 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 EdiPartyName: case OtherName: case X400Address: return new GeneralName(tag, Asn1Sequence.GetInstance(tagObj, false)); case DnsName: case Rfc822Name: case UniformResourceIdentifier: return new GeneralName(tag, DerIA5String.GetInstance(tagObj, false)); case DirectoryName: return new GeneralName(tag, X509Name.GetInstance(tagObj, true)); case IPAddress: return new GeneralName(tag, Asn1OctetString.GetInstance(tagObj, false)); case RegisteredID: return new GeneralName(tag, DerObjectIdentifier.GetInstance(tagObj, false)); default: throw new ArgumentException("unknown tag: " + tag); } } 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: " + BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.GetTypeName(obj), "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 (BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.StartsWith(ip, "::")) { ip = ip.Substring(1); } else if (BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.EndsWith(ip, "::")) { 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() { // directoryName is explicitly tagged as it is a CHOICE bool isExplicit = (tag == DirectoryName); return new DerTaggedObject(isExplicit, tag, obj); } } } #pragma warning restore #endif