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