18f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki/* 28f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Licensed to the Apache Software Foundation (ASF) under one or more 38f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * contributor license agreements. See the NOTICE file distributed with 48f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * this work for additional information regarding copyright ownership. 58f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * The ASF licenses this file to You under the Apache License, Version 2.0 68f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * (the "License"); you may not use this file except in compliance with 78f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * the License. You may obtain a copy of the License at 88f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 98f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Unless required by applicable law or agreed to in writing, software 128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * See the License for the specific language governing permissions and 158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * limitations under the License. 168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */ 178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 182269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornpackage com.android.internal.net; 192269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn 208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 218f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport android.util.Log; 228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 238f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport java.io.IOException; 248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 258f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport javax.security.auth.x500.X500Principal; 268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki/** 288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * A simple distinguished name(DN) parser. 298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is based on org.apache.harmony.security.x509.DNParser. It's customized to remove 318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * external references which are unnecessary for our requirements. 328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is only meant for extracting a string value from a DN. e.g. it doesn't support 348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * values in the hex-string style. 358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is used by {@link DomainNameValidator} only. However, in order to make this 378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * class visible from unit tests, it's made public. 382269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn * 392269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn * @hide 408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */ 418f028a94fc533e75077485a7d11a04e4de820335Makoto Onukipublic final class DNParser { 428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private static final String TAG = "DNParser"; 438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki /** DN to be parsed. */ 458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private final String dn; 468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // length of distinguished name string 488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private final int length; 498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private int pos, beg, end; 518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // tmp vars to store positions of the currently parsed item 538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private int cur; 548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // distinguished name chars 568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private char[] chars; 578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki /** 598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Exception message thrown when we failed to parse DN, which shouldn't happen because we 608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * only handle DNs that {@link X500Principal#getName} returns, which shouldn't be malformed. 618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */ 628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private static final String ERROR_PARSE_ERROR = "Failed to parse DN"; 638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki /** 658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Constructor. 668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * @param principal - {@link X500Principal} to be parsed 688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */ 698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki public DNParser(X500Principal principal) { 708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki this.dn = principal.getName(X500Principal.RFC2253); 718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki this.length = dn.length(); 728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // gets next attribute type: (ALPHA 1*keychar) / oid 758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private String nextAT() throws IOException { 768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // skip preceding space chars, they can present after 788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // comma or semicolon (compatibility with RFC 1779) 798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] == ' '; pos++) { 808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length) { 828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return null; // reached the end of DN 838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // mark the beginning of attribute type 868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg = pos; 878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // attribute type chars 898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) { 918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // we don't follow exact BNF syntax here: 928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // accept any char except space and '=' 938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos >= length) { 958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // unexpected end of DN 968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // mark the end of attribute type 1008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = pos; 1018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // skip trailing space chars between attribute type and '=' 1038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // (compatibility with RFC 1779) 1048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (chars[pos] == ' ') { 1058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) { 1068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (chars[pos] != '=' || pos == length) { 1098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // unexpected end of DN 1108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 1118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; //skip '=' char 1158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // skip space chars between '=' and attribute value 1178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // (compatibility with RFC 1779) 1188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] == ' '; pos++) { 1198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // in case of oid attribute type skip its prefix: "oid." or "OID." 1228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // (compatibility with RFC 1779) 1238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if ((end - beg > 4) && (chars[beg + 3] == '.') 1248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki && (chars[beg] == 'O' || chars[beg] == 'o') 1258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i') 1268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) { 1278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg += 4; 1288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, end - beg); 1318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION 1348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private String quotedAV() throws IOException { 1358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 1378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg = pos; 1388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = beg; 1398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki while (true) { 1408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length) { 1428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // unexpected end of DN 1438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 1448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (chars[pos] == '"') { 1478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // enclosing quotation was found 1488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 1498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 1508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (chars[pos] == '\\') { 1518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end] = getEscaped(); 1528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else { 1538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // shift char: required for string with escaped chars 1548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end] = chars[pos]; 1558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 1578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end++; 1588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // skip trailing space chars before comma or semicolon. 1618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // (compatibility with RFC 1779) 1628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] == ' '; pos++) { 1638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, end - beg); 1668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // gets hex string attribute value: "#" hexstring 1698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private String hexAV() throws IOException { 1708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos + 4 >= length) { 1728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // encoded byte array must be not less then 4 c 1738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 1748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg = pos; // store '#' position 1778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 1788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki while (true) { 1798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // check for end of attribute value 1818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // looks for space and component separators 1828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length || chars[pos] == '+' || chars[pos] == ',' 1838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki || chars[pos] == ';') { 1848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = pos; 1858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 1868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 1888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (chars[pos] == ' ') { 1898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = pos; 1908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 1918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // skip trailing space chars before comma or semicolon. 1928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // (compatibility with RFC 1779) 1938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] == ' '; pos++) { 1948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 1968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (chars[pos] >= 'A' && chars[pos] <= 'F') { 1978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[pos] += 32; //to low case 1988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 2018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // verify length of hex string 2048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // encoded byte array must be not less then 4 and must be even number 2058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki int hexLen = end - beg; // skip first '#' char 2068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (hexLen < 5 || (hexLen & 1) == 0) { 2078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 2088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // get byte encoding from string representation 2118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki byte[] encoded = new byte[hexLen / 2]; 2128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) { 2138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki encoded[i] = (byte) getByte(p); 2148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, hexLen); 2178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // gets string attribute value: *( stringchar / pair ) 2208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private String escapedAV() throws IOException { 2218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg = pos; 2238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = pos; 2248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki while (true) { 2258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos >= length) { 2278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // the end of DN has been found 2288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, end - beg); 2298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki switch (chars[pos]) { 2328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '+': 2338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ',': 2348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ';': 2358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // separator char has beed found 2368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, end - beg); 2378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '\\': 2388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // escaped char 2398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end++] = getEscaped(); 2408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 2418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 2428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ' ': 2438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // need to figure out whether space defines 2448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // the end of attribute value or not 2458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki cur = end; 2468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 2488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end++] = ' '; 2498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (; pos < length && chars[pos] == ' '; pos++) { 2518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end++] = ' '; 2528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length || chars[pos] == ',' || chars[pos] == '+' 2548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki || chars[pos] == ';') { 2558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // separator char or the end of DN has beed found 2568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return new String(chars, beg, cur - beg); 2578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 2598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki default: 2608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars[end++] = chars[pos]; 2618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 2628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // returns escaped char 2678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private char getEscaped() throws IOException { 2688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 2708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length) { 2718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 2728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki switch (chars[pos]) { 2758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '"': 2768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '\\': 2778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ',': 2788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '=': 2798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '+': 2808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '<': 2818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '>': 2828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '#': 2838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ';': 2848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ' ': 2858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '*': 2868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '%': 2878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '_': 2888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki //FIXME: escaping is allowed only for leading or trailing space char 2898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return chars[pos]; 2908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki default: 2918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // RFC doesn't explicitly say that escaped hex pair is 2928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // interpreted as UTF-8 char. It only contains an example of such DN. 2938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return getUTF8(); 2948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 2968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 2978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // decodes UTF-8 char 2988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // see http://www.unicode.org for UTF-8 bit distribution table 2998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private char getUTF8() throws IOException { 3008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki int res = getByte(pos); 3028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; //FIXME tmp 3038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (res < 128) { // one byte: 0-7F 3058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return (char) res; 3068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (res >= 192 && res <= 247) { 3078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki int count; 3098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (res <= 223) { // two bytes: C0-DF 3108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki count = 1; 3118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki res = res & 0x1F; 3128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (res <= 239) { // three bytes: E0-EF 3138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki count = 2; 3148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki res = res & 0x0F; 3158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else { // four bytes: F0-F7 3168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki count = 3; 3178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki res = res & 0x07; 3188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki int b; 3218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki for (int i = 0; i < count; i++) { 3228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 3238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length || chars[pos] != '\\') { 3248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 3258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 3278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b = getByte(pos); 3298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; //FIXME tmp 3308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if ((b & 0xC0) != 0x80) { 3318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 3328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki res = (res << 6) + (b & 0x3F); 3358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return (char) res; 3378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else { 3388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 3398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // Returns byte representation of a char pair 3438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // The char pair is composed of DN char in 3448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // specified 'position' and the next char 3458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // According to BNF syntax: 3468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // hexchar = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 3478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // / "a" / "b" / "c" / "d" / "e" / "f" 3488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private int getByte(int position) throws IOException { 3498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if ((position + 1) >= length) { 3518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // to avoid ArrayIndexOutOfBoundsException 3528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 3538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki int b1, b2; 3568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b1 = chars[position]; 3588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (b1 >= '0' && b1 <= '9') { 3598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b1 = b1 - '0'; 3608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (b1 >= 'a' && b1 <= 'f') { 3618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b1 = b1 - 87; // 87 = 'a' - 10 3628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (b1 >= 'A' && b1 <= 'F') { 3638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b1 = b1 - 55; // 55 = 'A' - 10 3648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else { 3658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 3668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b2 = chars[position + 1]; 3698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (b2 >= '0' && b2 <= '9') { 3708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b2 = b2 - '0'; 3718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (b2 >= 'a' && b2 <= 'f') { 3728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b2 = b2 - 87; // 87 = 'a' - 10 3738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (b2 >= 'A' && b2 <= 'F') { 3748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki b2 = b2 - 55; // 55 = 'A' - 10 3758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else { 3768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 3778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return (b1 << 4) + b2; 3808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 3818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki /** 3838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Parses the DN and returns the attribute value for an attribute type. 3848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * 3858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * @param attributeType attribute type to look for (e.g. "ca") 3868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * @return value of the attribute that first found, or null if none found 3878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */ 3888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki public String find(String attributeType) { 3898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki try { 3908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // Initialize internal state. 3918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos = 0; 3928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki beg = 0; 3938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki end = 0; 3948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki cur = 0; 3958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki chars = dn.toCharArray(); 3968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 3978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki String attType = nextAT(); 3988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (attType == null) { 3998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return null; 4008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki while (true) { 4028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki String attValue = ""; 4038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos == length) { 4058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return null; 4068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki switch (chars[pos]) { 4098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '"': 4108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki attValue = quotedAV(); 4118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 4128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '#': 4138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki attValue = hexAV(); 4148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 4158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case '+': 4168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ',': 4178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki case ';': // compatibility with RFC 1779: semicolon can separate RDNs 4188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki //empty attribute value 4198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki break; 4208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki default: 4218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki attValue = escapedAV(); 4228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (attributeType.equalsIgnoreCase(attType)) { 4258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return attValue; 4268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (pos >= length) { 4298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return null; 4308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (chars[pos] == ',' || chars[pos] == ';') { 4338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } else if (chars[pos] != '+') { 4348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 4358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 4378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki pos++; 4388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki attType = nextAT(); 4398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (attType == null) { 4408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki throw new IOException(ERROR_PARSE_ERROR); 4418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } catch (IOException e) { 4448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // Parse error shouldn't happen, because we only handle DNs that 4458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // X500Principal.getName() returns, which shouldn't be malformed. 4468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.e(TAG, "Failed to parse DN: " + dn); 4478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return null; 4488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 4508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki} 451