1faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath/* 2faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Licensed to the Apache Software Foundation (ASF) under one or more 3faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * contributor license agreements. See the NOTICE file distributed with 4faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * this work for additional information regarding copyright ownership. 5faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * The ASF licenses this file to You under the Apache License, Version 2.0 6faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * (the "License"); you may not use this file except in compliance with 7faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * the License. You may obtain a copy of the License at 8faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 9faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 10faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 11faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Unless required by applicable law or agreed to in writing, software 12faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 13faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * See the License for the specific language governing permissions and 15faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * limitations under the License. 16faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 17faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 18faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathpackage com.squareup.okhttp.internal.tls; 19faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 20faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport javax.security.auth.x500.X500Principal; 21faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 22faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath/** 23faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * A distinguished name (DN) parser. This parser only supports extracting a 24faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * string value from a DN. It doesn't support values in the hex-string style. 25faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 26faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathfinal class DistinguishedNameParser { 27faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private final String dn; 28faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private final int length; 29faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private int pos; 30faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private int beg; 31faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private int end; 32faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 33faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** Temporary variable to store positions of the currently parsed item. */ 34faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private int cur; 35faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 36faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** Distinguished name characters. */ 37faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private char[] chars; 38faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 39faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public DistinguishedNameParser(X500Principal principal) { 40faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // RFC2253 is used to ensure we get attributes in the reverse 41faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // order of the underlying ASN.1 encoding, so that the most 42faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // significant values of repeated attributes occur first. 43faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.dn = principal.getName(X500Principal.RFC2253); 44faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath this.length = this.dn.length(); 45faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 46faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 47faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // gets next attribute type: (ALPHA 1*keychar) / oid 48faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private String nextAT() { 49faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // skip preceding space chars, they can present after 50faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // comma or semicolon (compatibility with RFC 1779) 51faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] == ' '; pos++) { 52faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 53faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length) { 54faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; // reached the end of DN 55faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 56faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 57faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // mark the beginning of attribute type 58faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg = pos; 59faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 60faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // attribute type chars 61faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 62faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) { 63faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // we don't follow exact BNF syntax here: 64faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // accept any char except space and '=' 65faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 66faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos >= length) { 67faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 68faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 69faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 70faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // mark the end of attribute type 71faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = pos; 72faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 73faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // skip trailing space chars between attribute type and '=' 74faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // (compatibility with RFC 1779) 75faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (chars[pos] == ' ') { 76faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) { 77faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 78faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 79faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (chars[pos] != '=' || pos == length) { 80faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 81faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 82faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 83faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 84faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; //skip '=' char 85faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 86faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // skip space chars between '=' and attribute value 87faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // (compatibility with RFC 1779) 88faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] == ' '; pos++) { 89faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 90faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 91faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // in case of oid attribute type skip its prefix: "oid." or "OID." 92faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // (compatibility with RFC 1779) 93faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if ((end - beg > 4) && (chars[beg + 3] == '.') 94faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath && (chars[beg] == 'O' || chars[beg] == 'o') 95faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i') 96faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) { 97faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg += 4; 98faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 99faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 100faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, end - beg); 101faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 102faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 103faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION 104faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private String quotedAV() { 105faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 106faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg = pos; 107faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = beg; 108faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath while (true) { 109faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 110faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length) { 111faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 112faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 113faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 114faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (chars[pos] == '"') { 115faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // enclosing quotation was found 116faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 117faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 118faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (chars[pos] == '\\') { 119faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end] = getEscaped(); 120faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // shift char: required for string with escaped chars 122faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end] = chars[pos]; 123faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 124faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 125faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end++; 126faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 127faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // skip trailing space chars before comma or semicolon. 129faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // (compatibility with RFC 1779) 130faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] == ' '; pos++) { 131faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 132faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 133faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, end - beg); 134faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 135faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 136faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // gets hex string attribute value: "#" hexstring 137faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private String hexAV() { 138faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos + 4 >= length) { 139faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // encoded byte array must be not less then 4 c 140faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 141faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 142faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 143faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg = pos; // store '#' position 144faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 145faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath while (true) { 146faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 147faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // check for end of attribute value 148faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // looks for space and component separators 149faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length || chars[pos] == '+' || chars[pos] == ',' 150faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || chars[pos] == ';') { 151faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = pos; 152faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 153faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 154faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 155faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (chars[pos] == ' ') { 156faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = pos; 157faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 158faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // skip trailing space chars before comma or semicolon. 159faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // (compatibility with RFC 1779) 160faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] == ' '; pos++) { 161faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 162faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 163faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (chars[pos] >= 'A' && chars[pos] <= 'F') { 164faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[pos] += 32; //to low case 165faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 166faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 167faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 168faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 169faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 170faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // verify length of hex string 171faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // encoded byte array must be not less then 4 and must be even number 172faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int hexLen = end - beg; // skip first '#' char 173faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (hexLen < 5 || (hexLen & 1) == 0) { 174faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 175faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 176faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 177faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // get byte encoding from string representation 178faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath byte[] encoded = new byte[hexLen / 2]; 179faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) { 180faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath encoded[i] = (byte) getByte(p); 181faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 182faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 183faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, hexLen); 184faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 185faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 186faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // gets string attribute value: *( stringchar / pair ) 187faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private String escapedAV() { 188faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg = pos; 189faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = pos; 190faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath while (true) { 191faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos >= length) { 192faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // the end of DN has been found 193faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, end - beg); 194faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 195faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 196faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath switch (chars[pos]) { 197faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '+': 198faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ',': 199faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ';': 200faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // separator char has been found 201faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, end - beg); 202faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '\\': 203faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // escaped char 204faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end++] = getEscaped(); 205faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 206faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 207faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ' ': 208faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // need to figure out whether space defines 209faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // the end of attribute value or not 210faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath cur = end; 211faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 212faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 213faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end++] = ' '; 214faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 215faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (; pos < length && chars[pos] == ' '; pos++) { 216faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end++] = ' '; 217faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 218faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length || chars[pos] == ',' || chars[pos] == '+' 219faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath || chars[pos] == ';') { 220faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // separator char or the end of DN has been found 221faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return new String(chars, beg, cur - beg); 222faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 223faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 224faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath default: 225faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars[end++] = chars[pos]; 226faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 227faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 228faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 229faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 230faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 231faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // returns escaped char 232faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private char getEscaped() { 233faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 234faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length) { 235faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Unexpected end of DN: " + dn); 236faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 237faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 238faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath switch (chars[pos]) { 239faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '"': 240faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '\\': 241faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ',': 242faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '=': 243faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '+': 244faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '<': 245faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '>': 246faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '#': 247faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ';': 248faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ' ': 249faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '*': 250faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '%': 251faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '_': 252faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath //FIXME: escaping is allowed only for leading or trailing space char 253faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return chars[pos]; 254faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath default: 255faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // RFC doesn't explicitly say that escaped hex pair is 256faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // interpreted as UTF-8 char. It only contains an example of such DN. 257faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return getUTF8(); 258faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 259faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 260faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 261faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // decodes UTF-8 char 262faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // see http://www.unicode.org for UTF-8 bit distribution table 263faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private char getUTF8() { 264faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int res = getByte(pos); 265faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; //FIXME tmp 266faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 267faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (res < 128) { // one byte: 0-7F 268faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return (char) res; 269faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (res >= 192 && res <= 247) { 270faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 271faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int count; 272faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (res <= 223) { // two bytes: C0-DF 273faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath count = 1; 274faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath res = res & 0x1F; 275faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (res <= 239) { // three bytes: E0-EF 276faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath count = 2; 277faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath res = res & 0x0F; 278faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { // four bytes: F0-F7 279faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath count = 3; 280faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath res = res & 0x07; 281faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 282faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 283faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int b; 284faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath for (int i = 0; i < count; i++) { 285faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 286faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length || chars[pos] != '\\') { 287faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 288faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 289faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 290faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 291faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b = getByte(pos); 292faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; //FIXME tmp 293faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if ((b & 0xC0) != 0x80) { 294faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 295faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 296faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 297faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath res = (res << 6) + (b & 0x3F); 298faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 299faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return (char) res; 300faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 301faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 302faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 303faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 304faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 305faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // Returns byte representation of a char pair 306faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // The char pair is composed of DN char in 307faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // specified 'position' and the next char 308faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // According to BNF syntax: 309faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // hexchar = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 310faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // / "a" / "b" / "c" / "d" / "e" / "f" 311faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath private int getByte(int position) { 312faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (position + 1 >= length) { 313faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Malformed DN: " + dn); 314faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 315faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 316faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath int b1, b2; 317faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 318faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b1 = chars[position]; 319faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (b1 >= '0' && b1 <= '9') { 320faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b1 = b1 - '0'; 321faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (b1 >= 'a' && b1 <= 'f') { 322faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b1 = b1 - 87; // 87 = 'a' - 10 323faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (b1 >= 'A' && b1 <= 'F') { 324faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b1 = b1 - 55; // 55 = 'A' - 10 325faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 326faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Malformed DN: " + dn); 327faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 328faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 329faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b2 = chars[position + 1]; 330faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (b2 >= '0' && b2 <= '9') { 331faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b2 = b2 - '0'; 332faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (b2 >= 'a' && b2 <= 'f') { 333faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b2 = b2 - 87; // 87 = 'a' - 10 334faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (b2 >= 'A' && b2 <= 'F') { 335faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath b2 = b2 - 55; // 55 = 'A' - 10 336faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else { 337faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Malformed DN: " + dn); 338faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 339faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 340faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return (b1 << 4) + b2; 341faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 342faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 343faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath /** 344faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Parses the DN and returns the most significant attribute value 345faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * for an attribute type, or null if none found. 346faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * 347faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * @param attributeType attribute type to look for (e.g. "ca") 348faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath */ 349faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath public String findMostSpecific(String attributeType) { 350faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // Initialize internal state. 351faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos = 0; 352faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath beg = 0; 353faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath end = 0; 354faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath cur = 0; 355faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath chars = dn.toCharArray(); 356faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 357faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath String attType = nextAT(); 358faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (attType == null) { 359faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; 360faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 361faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath while (true) { 362faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath String attValue = ""; 363faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 364faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos == length) { 365faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; 366faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 367faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 368faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath switch (chars[pos]) { 369faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '"': 370faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath attValue = quotedAV(); 371faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 372faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '#': 373faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath attValue = hexAV(); 374faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 375faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case '+': 376faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ',': 377faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath case ';': // compatibility with RFC 1779: semicolon can separate RDNs 378faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath //empty attribute value 379faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath break; 380faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath default: 381faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath attValue = escapedAV(); 382faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 383faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 384faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // Values are ordered from most specific to least specific 385faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // due to the RFC2253 formatting. So take the first match 386faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath // we see. 387faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (attributeType.equalsIgnoreCase(attType)) { 388faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return attValue; 389faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 390faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 391faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (pos >= length) { 392faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath return null; 393faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 394faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 395faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (chars[pos] == ',' || chars[pos] == ';') { 396faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } else if (chars[pos] != '+') { 397faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Malformed DN: " + dn); 398faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 399faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath 400faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath pos++; 401faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath attType = nextAT(); 402faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath if (attType == null) { 403faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IllegalStateException("Malformed DN: " + dn); 404faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 405faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 406faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath } 407faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath} 408