157d73e33dc039f6fff06db52106a358192868060Jesse Wilson/* 257d73e33dc039f6fff06db52106a358192868060Jesse Wilson * Licensed to the Apache Software Foundation (ASF) under one or more 357d73e33dc039f6fff06db52106a358192868060Jesse Wilson * contributor license agreements. See the NOTICE file distributed with 457d73e33dc039f6fff06db52106a358192868060Jesse Wilson * this work for additional information regarding copyright ownership. 557d73e33dc039f6fff06db52106a358192868060Jesse Wilson * The ASF licenses this file to You under the Apache License, Version 2.0 657d73e33dc039f6fff06db52106a358192868060Jesse Wilson * (the "License"); you may not use this file except in compliance with 757d73e33dc039f6fff06db52106a358192868060Jesse Wilson * the License. You may obtain a copy of the License at 857d73e33dc039f6fff06db52106a358192868060Jesse Wilson * 957d73e33dc039f6fff06db52106a358192868060Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 1057d73e33dc039f6fff06db52106a358192868060Jesse Wilson * 1157d73e33dc039f6fff06db52106a358192868060Jesse Wilson * Unless required by applicable law or agreed to in writing, software 1257d73e33dc039f6fff06db52106a358192868060Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 1357d73e33dc039f6fff06db52106a358192868060Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1457d73e33dc039f6fff06db52106a358192868060Jesse Wilson * See the License for the specific language governing permissions and 1557d73e33dc039f6fff06db52106a358192868060Jesse Wilson * limitations under the License. 1657d73e33dc039f6fff06db52106a358192868060Jesse Wilson */ 1757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 1857d73e33dc039f6fff06db52106a358192868060Jesse Wilsonpackage javax.net.ssl; 1957d73e33dc039f6fff06db52106a358192868060Jesse Wilson 2057d73e33dc039f6fff06db52106a358192868060Jesse Wilsonimport javax.security.auth.x500.X500Principal; 2157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 2257d73e33dc039f6fff06db52106a358192868060Jesse Wilson/** 2357d73e33dc039f6fff06db52106a358192868060Jesse Wilson * A distinguished name (DN) parser. This parser only supports extracting a 2457d73e33dc039f6fff06db52106a358192868060Jesse Wilson * string value from a DN. It doesn't support values in the hex-string style. 2557d73e33dc039f6fff06db52106a358192868060Jesse Wilson * 2657d73e33dc039f6fff06db52106a358192868060Jesse Wilson * @hide 2757d73e33dc039f6fff06db52106a358192868060Jesse Wilson */ 2857d73e33dc039f6fff06db52106a358192868060Jesse Wilsonpublic final class DistinguishedNameParser { 2957d73e33dc039f6fff06db52106a358192868060Jesse Wilson private final String dn; 3057d73e33dc039f6fff06db52106a358192868060Jesse Wilson private final int length; 3157d73e33dc039f6fff06db52106a358192868060Jesse Wilson private int pos; 3257d73e33dc039f6fff06db52106a358192868060Jesse Wilson private int beg; 3357d73e33dc039f6fff06db52106a358192868060Jesse Wilson private int end; 3457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 3557d73e33dc039f6fff06db52106a358192868060Jesse Wilson /** tmp vars to store positions of the currently parsed item */ 3657d73e33dc039f6fff06db52106a358192868060Jesse Wilson private int cur; 3757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 3857d73e33dc039f6fff06db52106a358192868060Jesse Wilson /** distinguished name chars */ 3957d73e33dc039f6fff06db52106a358192868060Jesse Wilson private char[] chars; 4057d73e33dc039f6fff06db52106a358192868060Jesse Wilson 4157d73e33dc039f6fff06db52106a358192868060Jesse Wilson public DistinguishedNameParser(X500Principal principal) { 428db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // RFC2253 is used to ensure we get attributes in the reverse 438db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // order of the underlying ASN.1 encoding, so that the most 448db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // significant values of repeated attributes occur first. 4557d73e33dc039f6fff06db52106a358192868060Jesse Wilson this.dn = principal.getName(X500Principal.RFC2253); 4657d73e33dc039f6fff06db52106a358192868060Jesse Wilson this.length = this.dn.length(); 4757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 4857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 4957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // gets next attribute type: (ALPHA 1*keychar) / oid 5057d73e33dc039f6fff06db52106a358192868060Jesse Wilson private String nextAT() { 5157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // skip preceding space chars, they can present after 5257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // comma or semicolon (compatibility with RFC 1779) 5357d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] == ' '; pos++) { 5457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 5557d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length) { 5657d73e33dc039f6fff06db52106a358192868060Jesse Wilson return null; // reached the end of DN 5757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 5857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 5957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // mark the beginning of attribute type 6057d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg = pos; 6157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 6257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // attribute type chars 6357d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 6457d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) { 6557d73e33dc039f6fff06db52106a358192868060Jesse Wilson // we don't follow exact BNF syntax here: 6657d73e33dc039f6fff06db52106a358192868060Jesse Wilson // accept any char except space and '=' 6757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 6857d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos >= length) { 6957d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 7057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 7157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 7257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // mark the end of attribute type 7357d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = pos; 7457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 7557d73e33dc039f6fff06db52106a358192868060Jesse Wilson // skip trailing space chars between attribute type and '=' 7657d73e33dc039f6fff06db52106a358192868060Jesse Wilson // (compatibility with RFC 1779) 7757d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (chars[pos] == ' ') { 7857d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) { 7957d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 8057d73e33dc039f6fff06db52106a358192868060Jesse Wilson 8157d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (chars[pos] != '=' || pos == length) { 8257d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 8357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 8457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 8557d73e33dc039f6fff06db52106a358192868060Jesse Wilson 8657d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; //skip '=' char 8757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 8857d73e33dc039f6fff06db52106a358192868060Jesse Wilson // skip space chars between '=' and attribute value 8957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // (compatibility with RFC 1779) 9057d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] == ' '; pos++) { 9157d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 9257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 9357d73e33dc039f6fff06db52106a358192868060Jesse Wilson // in case of oid attribute type skip its prefix: "oid." or "OID." 9457d73e33dc039f6fff06db52106a358192868060Jesse Wilson // (compatibility with RFC 1779) 9557d73e33dc039f6fff06db52106a358192868060Jesse Wilson if ((end - beg > 4) && (chars[beg + 3] == '.') 9657d73e33dc039f6fff06db52106a358192868060Jesse Wilson && (chars[beg] == 'O' || chars[beg] == 'o') 9757d73e33dc039f6fff06db52106a358192868060Jesse Wilson && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i') 9857d73e33dc039f6fff06db52106a358192868060Jesse Wilson && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) { 9957d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg += 4; 10057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 10157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 10257d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, end - beg); 10357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 10457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 10557d73e33dc039f6fff06db52106a358192868060Jesse Wilson // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION 10657d73e33dc039f6fff06db52106a358192868060Jesse Wilson private String quotedAV() { 10757d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 10857d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg = pos; 10957d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = beg; 11057d73e33dc039f6fff06db52106a358192868060Jesse Wilson while (true) { 11157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 11257d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length) { 11357d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 11457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 11557d73e33dc039f6fff06db52106a358192868060Jesse Wilson 11657d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (chars[pos] == '"') { 11757d73e33dc039f6fff06db52106a358192868060Jesse Wilson // enclosing quotation was found 11857d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 11957d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 12057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (chars[pos] == '\\') { 12157d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end] = getEscaped(); 12257d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else { 12357d73e33dc039f6fff06db52106a358192868060Jesse Wilson // shift char: required for string with escaped chars 12457d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end] = chars[pos]; 12557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 12657d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 12757d73e33dc039f6fff06db52106a358192868060Jesse Wilson end++; 12857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 12957d73e33dc039f6fff06db52106a358192868060Jesse Wilson 13057d73e33dc039f6fff06db52106a358192868060Jesse Wilson // skip trailing space chars before comma or semicolon. 13157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // (compatibility with RFC 1779) 13257d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] == ' '; pos++) { 13357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 13457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 13557d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, end - beg); 13657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 13757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 13857d73e33dc039f6fff06db52106a358192868060Jesse Wilson // gets hex string attribute value: "#" hexstring 13957d73e33dc039f6fff06db52106a358192868060Jesse Wilson private String hexAV() { 14057d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos + 4 >= length) { 14157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // encoded byte array must be not less then 4 c 14257d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 14357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 14457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 14557d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg = pos; // store '#' position 14657d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 14757d73e33dc039f6fff06db52106a358192868060Jesse Wilson while (true) { 14857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 14957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // check for end of attribute value 15057d73e33dc039f6fff06db52106a358192868060Jesse Wilson // looks for space and component separators 15157d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length || chars[pos] == '+' || chars[pos] == ',' 15257d73e33dc039f6fff06db52106a358192868060Jesse Wilson || chars[pos] == ';') { 15357d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = pos; 15457d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 15557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 15657d73e33dc039f6fff06db52106a358192868060Jesse Wilson 15757d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (chars[pos] == ' ') { 15857d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = pos; 15957d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 16057d73e33dc039f6fff06db52106a358192868060Jesse Wilson // skip trailing space chars before comma or semicolon. 16157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // (compatibility with RFC 1779) 16257d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] == ' '; pos++) { 16357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 16457d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 16557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (chars[pos] >= 'A' && chars[pos] <= 'F') { 16657d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[pos] += 32; //to low case 16757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 16857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 16957d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 17057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 17157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 17257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // verify length of hex string 17357d73e33dc039f6fff06db52106a358192868060Jesse Wilson // encoded byte array must be not less then 4 and must be even number 17457d73e33dc039f6fff06db52106a358192868060Jesse Wilson int hexLen = end - beg; // skip first '#' char 17557d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (hexLen < 5 || (hexLen & 1) == 0) { 17657d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 17757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 17857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 17957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // get byte encoding from string representation 18057d73e33dc039f6fff06db52106a358192868060Jesse Wilson byte[] encoded = new byte[hexLen / 2]; 18157d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) { 18257d73e33dc039f6fff06db52106a358192868060Jesse Wilson encoded[i] = (byte) getByte(p); 18357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 18457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 18557d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, hexLen); 18657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 18757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 18857d73e33dc039f6fff06db52106a358192868060Jesse Wilson // gets string attribute value: *( stringchar / pair ) 18957d73e33dc039f6fff06db52106a358192868060Jesse Wilson private String escapedAV() { 19057d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg = pos; 19157d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = pos; 19257d73e33dc039f6fff06db52106a358192868060Jesse Wilson while (true) { 19357d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos >= length) { 19457d73e33dc039f6fff06db52106a358192868060Jesse Wilson // the end of DN has been found 19557d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, end - beg); 19657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 19757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 19857d73e33dc039f6fff06db52106a358192868060Jesse Wilson switch (chars[pos]) { 19957d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '+': 20057d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ',': 20157d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ';': 2025692d40be0e19ef1d0cc988a579576496adf5237Brian Carlstrom // separator char has been found 20357d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, end - beg); 20457d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '\\': 20557d73e33dc039f6fff06db52106a358192868060Jesse Wilson // escaped char 20657d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end++] = getEscaped(); 20757d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 20857d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 20957d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ' ': 21057d73e33dc039f6fff06db52106a358192868060Jesse Wilson // need to figure out whether space defines 21157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // the end of attribute value or not 21257d73e33dc039f6fff06db52106a358192868060Jesse Wilson cur = end; 21357d73e33dc039f6fff06db52106a358192868060Jesse Wilson 21457d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 21557d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end++] = ' '; 21657d73e33dc039f6fff06db52106a358192868060Jesse Wilson 21757d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (; pos < length && chars[pos] == ' '; pos++) { 21857d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end++] = ' '; 21957d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 22057d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length || chars[pos] == ',' || chars[pos] == '+' 22157d73e33dc039f6fff06db52106a358192868060Jesse Wilson || chars[pos] == ';') { 2225692d40be0e19ef1d0cc988a579576496adf5237Brian Carlstrom // separator char or the end of DN has been found 22357d73e33dc039f6fff06db52106a358192868060Jesse Wilson return new String(chars, beg, cur - beg); 22457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 22557d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 22657d73e33dc039f6fff06db52106a358192868060Jesse Wilson default: 22757d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars[end++] = chars[pos]; 22857d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 22957d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 23057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 23157d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 23257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 23357d73e33dc039f6fff06db52106a358192868060Jesse Wilson // returns escaped char 23457d73e33dc039f6fff06db52106a358192868060Jesse Wilson private char getEscaped() { 23557d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 23657d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length) { 23757d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Unexpected end of DN: " + dn); 23857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 23957d73e33dc039f6fff06db52106a358192868060Jesse Wilson 24057d73e33dc039f6fff06db52106a358192868060Jesse Wilson switch (chars[pos]) { 24157d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '"': 24257d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '\\': 24357d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ',': 24457d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '=': 24557d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '+': 24657d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '<': 24757d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '>': 24857d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '#': 24957d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ';': 25057d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ' ': 25157d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '*': 25257d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '%': 25357d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '_': 25457d73e33dc039f6fff06db52106a358192868060Jesse Wilson //FIXME: escaping is allowed only for leading or trailing space char 25557d73e33dc039f6fff06db52106a358192868060Jesse Wilson return chars[pos]; 25657d73e33dc039f6fff06db52106a358192868060Jesse Wilson default: 25757d73e33dc039f6fff06db52106a358192868060Jesse Wilson // RFC doesn't explicitly say that escaped hex pair is 25857d73e33dc039f6fff06db52106a358192868060Jesse Wilson // interpreted as UTF-8 char. It only contains an example of such DN. 25957d73e33dc039f6fff06db52106a358192868060Jesse Wilson return getUTF8(); 26057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 26157d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 26257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 26357d73e33dc039f6fff06db52106a358192868060Jesse Wilson // decodes UTF-8 char 26457d73e33dc039f6fff06db52106a358192868060Jesse Wilson // see http://www.unicode.org for UTF-8 bit distribution table 26557d73e33dc039f6fff06db52106a358192868060Jesse Wilson private char getUTF8() { 26657d73e33dc039f6fff06db52106a358192868060Jesse Wilson int res = getByte(pos); 26757d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; //FIXME tmp 26857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 26957d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (res < 128) { // one byte: 0-7F 27057d73e33dc039f6fff06db52106a358192868060Jesse Wilson return (char) res; 27157d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (res >= 192 && res <= 247) { 27257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 27357d73e33dc039f6fff06db52106a358192868060Jesse Wilson int count; 27457d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (res <= 223) { // two bytes: C0-DF 27557d73e33dc039f6fff06db52106a358192868060Jesse Wilson count = 1; 27657d73e33dc039f6fff06db52106a358192868060Jesse Wilson res = res & 0x1F; 27757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (res <= 239) { // three bytes: E0-EF 27857d73e33dc039f6fff06db52106a358192868060Jesse Wilson count = 2; 27957d73e33dc039f6fff06db52106a358192868060Jesse Wilson res = res & 0x0F; 28057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else { // four bytes: F0-F7 28157d73e33dc039f6fff06db52106a358192868060Jesse Wilson count = 3; 28257d73e33dc039f6fff06db52106a358192868060Jesse Wilson res = res & 0x07; 28357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 28457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 28557d73e33dc039f6fff06db52106a358192868060Jesse Wilson int b; 28657d73e33dc039f6fff06db52106a358192868060Jesse Wilson for (int i = 0; i < count; i++) { 28757d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 28857d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length || chars[pos] != '\\') { 28957d73e33dc039f6fff06db52106a358192868060Jesse Wilson return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 29057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 29157d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 29257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 29357d73e33dc039f6fff06db52106a358192868060Jesse Wilson b = getByte(pos); 29457d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; //FIXME tmp 29557d73e33dc039f6fff06db52106a358192868060Jesse Wilson if ((b & 0xC0) != 0x80) { 29657d73e33dc039f6fff06db52106a358192868060Jesse Wilson return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 29757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 29857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 29957d73e33dc039f6fff06db52106a358192868060Jesse Wilson res = (res << 6) + (b & 0x3F); 30057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 30157d73e33dc039f6fff06db52106a358192868060Jesse Wilson return (char) res; 30257d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else { 30357d73e33dc039f6fff06db52106a358192868060Jesse Wilson return 0x3F; //FIXME failed to decode UTF-8 char - return '?' 30457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 30557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 30657d73e33dc039f6fff06db52106a358192868060Jesse Wilson 30757d73e33dc039f6fff06db52106a358192868060Jesse Wilson // Returns byte representation of a char pair 30857d73e33dc039f6fff06db52106a358192868060Jesse Wilson // The char pair is composed of DN char in 30957d73e33dc039f6fff06db52106a358192868060Jesse Wilson // specified 'position' and the next char 31057d73e33dc039f6fff06db52106a358192868060Jesse Wilson // According to BNF syntax: 31157d73e33dc039f6fff06db52106a358192868060Jesse Wilson // hexchar = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 31257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // / "a" / "b" / "c" / "d" / "e" / "f" 31357d73e33dc039f6fff06db52106a358192868060Jesse Wilson private int getByte(int position) { 31457d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (position + 1 >= length) { 31557d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Malformed DN: " + dn); 31657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 31757d73e33dc039f6fff06db52106a358192868060Jesse Wilson 31857d73e33dc039f6fff06db52106a358192868060Jesse Wilson int b1, b2; 31957d73e33dc039f6fff06db52106a358192868060Jesse Wilson 32057d73e33dc039f6fff06db52106a358192868060Jesse Wilson b1 = chars[position]; 32157d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (b1 >= '0' && b1 <= '9') { 32257d73e33dc039f6fff06db52106a358192868060Jesse Wilson b1 = b1 - '0'; 32357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (b1 >= 'a' && b1 <= 'f') { 32457d73e33dc039f6fff06db52106a358192868060Jesse Wilson b1 = b1 - 87; // 87 = 'a' - 10 32557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (b1 >= 'A' && b1 <= 'F') { 32657d73e33dc039f6fff06db52106a358192868060Jesse Wilson b1 = b1 - 55; // 55 = 'A' - 10 32757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else { 32857d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Malformed DN: " + dn); 32957d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 33057d73e33dc039f6fff06db52106a358192868060Jesse Wilson 33157d73e33dc039f6fff06db52106a358192868060Jesse Wilson b2 = chars[position + 1]; 33257d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (b2 >= '0' && b2 <= '9') { 33357d73e33dc039f6fff06db52106a358192868060Jesse Wilson b2 = b2 - '0'; 33457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (b2 >= 'a' && b2 <= 'f') { 33557d73e33dc039f6fff06db52106a358192868060Jesse Wilson b2 = b2 - 87; // 87 = 'a' - 10 33657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (b2 >= 'A' && b2 <= 'F') { 33757d73e33dc039f6fff06db52106a358192868060Jesse Wilson b2 = b2 - 55; // 55 = 'A' - 10 33857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else { 33957d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Malformed DN: " + dn); 34057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 34157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 34257d73e33dc039f6fff06db52106a358192868060Jesse Wilson return (b1 << 4) + b2; 34357d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 34457d73e33dc039f6fff06db52106a358192868060Jesse Wilson 34557d73e33dc039f6fff06db52106a358192868060Jesse Wilson /** 3461331404bf45cb2f220ee9aa2c0c108ce59453a74Brian Carlstrom * Parses the DN and returns the most significant attribute value 3471331404bf45cb2f220ee9aa2c0c108ce59453a74Brian Carlstrom * for an attribute type, or null if none found. 34857d73e33dc039f6fff06db52106a358192868060Jesse Wilson * 34957d73e33dc039f6fff06db52106a358192868060Jesse Wilson * @param attributeType attribute type to look for (e.g. "ca") 35057d73e33dc039f6fff06db52106a358192868060Jesse Wilson */ 3511331404bf45cb2f220ee9aa2c0c108ce59453a74Brian Carlstrom public String findMostSpecific(String attributeType) { 35257d73e33dc039f6fff06db52106a358192868060Jesse Wilson // Initialize internal state. 35357d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos = 0; 35457d73e33dc039f6fff06db52106a358192868060Jesse Wilson beg = 0; 35557d73e33dc039f6fff06db52106a358192868060Jesse Wilson end = 0; 35657d73e33dc039f6fff06db52106a358192868060Jesse Wilson cur = 0; 35757d73e33dc039f6fff06db52106a358192868060Jesse Wilson chars = dn.toCharArray(); 35857d73e33dc039f6fff06db52106a358192868060Jesse Wilson 35957d73e33dc039f6fff06db52106a358192868060Jesse Wilson String attType = nextAT(); 36057d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (attType == null) { 36157d73e33dc039f6fff06db52106a358192868060Jesse Wilson return null; 36257d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 36357d73e33dc039f6fff06db52106a358192868060Jesse Wilson while (true) { 36457d73e33dc039f6fff06db52106a358192868060Jesse Wilson String attValue = ""; 36557d73e33dc039f6fff06db52106a358192868060Jesse Wilson 36657d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos == length) { 3678db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom return null; 36857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 36957d73e33dc039f6fff06db52106a358192868060Jesse Wilson 37057d73e33dc039f6fff06db52106a358192868060Jesse Wilson switch (chars[pos]) { 37157d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '"': 37257d73e33dc039f6fff06db52106a358192868060Jesse Wilson attValue = quotedAV(); 37357d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 37457d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '#': 37557d73e33dc039f6fff06db52106a358192868060Jesse Wilson attValue = hexAV(); 37657d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 37757d73e33dc039f6fff06db52106a358192868060Jesse Wilson case '+': 37857d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ',': 37957d73e33dc039f6fff06db52106a358192868060Jesse Wilson case ';': // compatibility with RFC 1779: semicolon can separate RDNs 38057d73e33dc039f6fff06db52106a358192868060Jesse Wilson //empty attribute value 38157d73e33dc039f6fff06db52106a358192868060Jesse Wilson break; 38257d73e33dc039f6fff06db52106a358192868060Jesse Wilson default: 38357d73e33dc039f6fff06db52106a358192868060Jesse Wilson attValue = escapedAV(); 38457d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 38557d73e33dc039f6fff06db52106a358192868060Jesse Wilson 3868db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // Values are ordered from most specific to least specific 3878db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // due to the RFC2253 formatting. So take the first match 3888db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom // we see. 38957d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (attributeType.equalsIgnoreCase(attType)) { 3908db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom return attValue; 39157d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 39257d73e33dc039f6fff06db52106a358192868060Jesse Wilson 39357d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (pos >= length) { 3948db6f195ccb99e15f0de3b85aa2f670427497081Brian Carlstrom return null; 39557d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 39657d73e33dc039f6fff06db52106a358192868060Jesse Wilson 39757d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (chars[pos] == ',' || chars[pos] == ';') { 39857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } else if (chars[pos] != '+') { 39957d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Malformed DN: " + dn); 40057d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 40157d73e33dc039f6fff06db52106a358192868060Jesse Wilson 40257d73e33dc039f6fff06db52106a358192868060Jesse Wilson pos++; 40357d73e33dc039f6fff06db52106a358192868060Jesse Wilson attType = nextAT(); 40457d73e33dc039f6fff06db52106a358192868060Jesse Wilson if (attType == null) { 40557d73e33dc039f6fff06db52106a358192868060Jesse Wilson throw new IllegalStateException("Malformed DN: " + dn); 40657d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 40757d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 40857d73e33dc039f6fff06db52106a358192868060Jesse Wilson } 40957d73e33dc039f6fff06db52106a358192868060Jesse Wilson} 410