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/GeneralName.java')
-rw-r--r--core/src/main/java/org/spongycastle/asn1/x509/GeneralName.java439
1 files changed, 439 insertions, 0 deletions
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);
+ }
+ }
+}