| /* GENERATED SOURCE. DO NOT MODIFY. */ |
| package com.android.org.bouncycastle.asn1.x500.style; |
| |
| import java.io.IOException; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| |
| import com.android.org.bouncycastle.asn1.ASN1Encodable; |
| import com.android.org.bouncycastle.asn1.ASN1Encoding; |
| import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; |
| import com.android.org.bouncycastle.asn1.ASN1Primitive; |
| import com.android.org.bouncycastle.asn1.ASN1String; |
| import com.android.org.bouncycastle.asn1.DERUniversalString; |
| import com.android.org.bouncycastle.asn1.x500.AttributeTypeAndValue; |
| import com.android.org.bouncycastle.asn1.x500.RDN; |
| import com.android.org.bouncycastle.asn1.x500.X500NameBuilder; |
| import com.android.org.bouncycastle.asn1.x500.X500NameStyle; |
| import com.android.org.bouncycastle.util.Strings; |
| import com.android.org.bouncycastle.util.encoders.Hex; |
| |
| /** |
| * @hide This class is not part of the Android public SDK API |
| */ |
| 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 |
| { |
| if (rdn.getFirst() != null) |
| { |
| 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); |
| |
| if (value.length() > 0 && value.charAt(0) == '#') |
| { |
| ASN1Primitive obj = decodeObject(value); |
| |
| if (obj instanceof ASN1String) |
| { |
| value = Strings.toLowerCase(((ASN1String)obj).getString()); |
| } |
| } |
| |
| if (value.length() > 1) |
| { |
| int start = 0; |
| while (start + 1 < value.length() && value.charAt(start) == '\\' && value.charAt(start + 1) == ' ') |
| { |
| start += 2; |
| } |
| |
| int end = value.length() - 1; |
| while (end - 1 > 0 && value.charAt(end - 1) == '\\' && value.charAt(end) == ' ') |
| { |
| end -= 2; |
| } |
| |
| if (start > 0 || end < value.length() - 1) |
| { |
| value = value.substring(start, end + 1); |
| } |
| } |
| |
| 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; |
| } |
| } |