All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aoju.bus.http.secure.DistinguishedNameParser Maven / Gradle / Ivy

/*********************************************************************************
 *                                                                               *
 * The MIT License (MIT)                                                         *
 *                                                                               *
 * Copyright (c) 2015-2022 aoju.org and other contributors.                      *
 *                                                                               *
 * Permission is hereby granted, free of charge, to any person obtaining a copy  *
 * of this software and associated documentation files (the "Software"), to deal *
 * in the Software without restriction, including without limitation the rights  *
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     *
 * copies of the Software, and to permit persons to whom the Software is         *
 * furnished to do so, subject to the following conditions:                      *
 *                                                                               *
 * The above copyright notice and this permission notice shall be included in    *
 * all copies or substantial portions of the Software.                           *
 *                                                                               *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     *
 * THE SOFTWARE.                                                                 *
 *                                                                               *
 ********************************************************************************/
package org.aoju.bus.http.secure;

import org.aoju.bus.core.lang.Normal;
import org.aoju.bus.core.lang.Symbol;

import javax.security.auth.x500.X500Principal;

/**
 * 专有名称(DN)解析器。该解析器只支持从DN中提取字符串值。
 * 它不支持十六进制字符串样式的值.
 *
 * @author Kimi Liu
 * @since Java 17+
 */
public class DistinguishedNameParser {

    private final String dn;
    private final int length;
    private int pos;
    private int beg;
    private int end;

    /**
     * 临时变量,用于存储当前已解析项的位置
     */
    private int cur;

    /**
     * 专有名称的字符
     */
    private char[] chars;

    DistinguishedNameParser(X500Principal principal) {
        this.dn = principal.getName(X500Principal.RFC2253);
        this.length = this.dn.length();
    }

    private String nextAT() {
        for (; pos < length && chars[pos] == Symbol.C_SPACE; pos++) {
        }
        if (pos == length) {
            return null;
        }

        beg = pos;

        pos++;
        for (; pos < length && chars[pos] != Symbol.C_EQUAL && chars[pos] != Symbol.C_SPACE; pos++) {
        }
        if (pos >= length) {
            throw new IllegalStateException("Unexpected end of DN: " + dn);
        }

        end = pos;

        if (chars[pos] == Symbol.C_SPACE) {
            for (; pos < length && chars[pos] != Symbol.C_EQUAL && chars[pos] == Symbol.C_SPACE; pos++) {
            }

            if (chars[pos] != Symbol.C_EQUAL || pos == length) {
                throw new IllegalStateException("Unexpected end of DN: " + dn);
            }
        }

        pos++;

        for (; pos < length && chars[pos] == Symbol.C_SPACE; pos++) {

        }

        if ((end - beg > 4) && (chars[beg + 3] == Symbol.C_DOT)
                && (chars[beg] == 'O' || chars[beg] == 'o')
                && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
                && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
            beg += 4;
        }

        return new String(chars, beg, end - beg);
    }

    private String quotedAV() {
        pos++;
        beg = pos;
        end = beg;
        while (true) {

            if (pos == length) {
                throw new IllegalStateException("Unexpected end of DN: " + dn);
            }

            if (chars[pos] == Symbol.C_DOUBLE_QUOTES) {
                pos++;
                break;
            } else if (chars[pos] == Symbol.C_BACKSLASH) {
                chars[end] = getEscaped();
            } else {
                chars[end] = chars[pos];
            }
            pos++;
            end++;
        }

        for (; pos < length && chars[pos] == Symbol.C_SPACE; pos++) {
        }

        return new String(chars, beg, end - beg);
    }

    private String hexAV() {
        if (pos + 4 >= length) {
            throw new IllegalStateException("Unexpected end of DN: " + dn);
        }

        beg = pos;
        pos++;
        while (true) {
            if (pos == length || chars[pos] == Symbol.C_PLUS || chars[pos] == Symbol.C_COMMA
                    || chars[pos] == Symbol.C_SEMICOLON) {
                end = pos;
                break;
            }

            if (chars[pos] == Symbol.C_SPACE) {
                end = pos;
                pos++;
                for (; pos < length && chars[pos] == Symbol.C_SPACE; pos++) {
                }
                break;
            } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
                chars[pos] += Normal._32;
            }

            pos++;
        }

        int hexLen = end - beg;
        if (hexLen < 5 || (hexLen & 1) == 0) {
            throw new IllegalStateException("Unexpected end of DN: " + dn);
        }

        // get byte encoding from string representation
        byte[] encoded = new byte[hexLen / 2];
        for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
            encoded[i] = (byte) getByte(p);
        }

        return new String(chars, beg, hexLen);
    }

    // gets string attribute value: *( stringchar / pair )
    private String escapedAV() {
        beg = pos;
        end = pos;
        while (true) {
            if (pos >= length) {
                // the end of DN has been found
                return new String(chars, beg, end - beg);
            }

            switch (chars[pos]) {
                case Symbol.C_PLUS:
                case Symbol.C_COMMA:
                case Symbol.C_SEMICOLON:
                    return new String(chars, beg, end - beg);
                case Symbol.C_BACKSLASH:
                    chars[end++] = getEscaped();
                    pos++;
                    break;
                case Symbol.C_SPACE:
                    cur = end;

                    pos++;
                    chars[end++] = Symbol.C_SPACE;

                    for (; pos < length && chars[pos] == Symbol.C_SPACE; pos++) {
                        chars[end++] = Symbol.C_SPACE;
                    }
                    if (pos == length || chars[pos] == Symbol.C_COMMA || chars[pos] == Symbol.C_PLUS
                            || chars[pos] == Symbol.C_SEMICOLON) {
                        return new String(chars, beg, cur - beg);
                    }
                    break;
                default:
                    chars[end++] = chars[pos];
                    pos++;
            }
        }
    }

    // returns escaped char
    private char getEscaped() {
        pos++;
        if (pos == length) {
            throw new IllegalStateException("Unexpected end of DN: " + dn);
        }

        switch (chars[pos]) {
            case Symbol.C_DOUBLE_QUOTES:
            case Symbol.C_BACKSLASH:
            case Symbol.C_COMMA:
            case Symbol.C_EQUAL:
            case Symbol.C_PLUS:
            case Symbol.C_LT:
            case Symbol.C_GT:
            case Symbol.C_SHAPE:
            case Symbol.C_SEMICOLON:
            case Symbol.C_SPACE:
            case Symbol.C_STAR:
            case Symbol.C_PERCENT:
            case Symbol.C_UNDERLINE:
                return chars[pos];
            default:
                return getUTF8();
        }
    }

    private char getUTF8() {
        int res = getByte(pos);
        pos++; //FIXME tmp

        if (res < Normal._128) {
            return (char) res;
        } else if (res >= 192 && res <= 247) {

            int count;
            if (res <= 223) {
                count = 1;
                res = res & 0x1F;
            } else if (res <= 239) {
                count = 2;
                res = res & 0x0F;
            } else {
                count = 3;
                res = res & 0x07;
            }

            int b;
            for (int i = 0; i < count; i++) {
                pos++;
                if (pos == length || chars[pos] != Symbol.C_BACKSLASH) {
                    return 0x3F;
                }
                pos++;

                b = getByte(pos);
                pos++; //FIXME tmp
                if ((b & 0xC0) != 0x80) {
                    return 0x3F;
                }

                res = (res << 6) + (b & 0x3F);
            }
            return (char) res;
        } else {
            return 0x3F;
        }
    }

    // Returns byte representation of a char pair
    // The char pair is composed of DN char in
    // specified 'position' and the next char
    // According to BNF syntax:
    // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
    //                    / "a" / "b" / "c" / "d" / "e" / "f"
    private int getByte(int position) {
        if (position + 1 >= length) {
            throw new IllegalStateException("Malformed DN: " + dn);
        }

        int b1, b2;

        b1 = chars[position];
        if (b1 >= Symbol.C_ZERO && b1 <= Symbol.C_NINE) {
            b1 = b1 - Symbol.C_ZERO;
        } else if (b1 >= 'a' && b1 <= 'f') {
            b1 = b1 - 87; // 87 = 'a' - 10
        } else if (b1 >= 'A' && b1 <= 'F') {
            b1 = b1 - 55; // 55 = 'A' - 10
        } else {
            throw new IllegalStateException("Malformed DN: " + dn);
        }

        b2 = chars[position + 1];
        if (b2 >= Symbol.C_ZERO && b2 <= Symbol.C_NINE) {
            b2 = b2 - Symbol.C_ZERO;
        } else if (b2 >= 'a' && b2 <= 'f') {
            b2 = b2 - 87; // 87 = 'a' - 10
        } else if (b2 >= 'A' && b2 <= 'F') {
            b2 = b2 - 55; // 55 = 'A' - 10
        } else {
            throw new IllegalStateException("Malformed DN: " + dn);
        }

        return (b1 << 4) + b2;
    }

    /**
     * Parses the DN and returns the most significant attribute value for an attribute type, or null
     * if none found.
     *
     * @param attributeType attribute type to look for (e.g. "ca")
     */
    public String findMostSpecific(String attributeType) {
        // Initialize internal state.
        pos = 0;
        beg = 0;
        end = 0;
        cur = 0;
        chars = dn.toCharArray();

        String attType = nextAT();
        if (null == attType) {
            return null;
        }
        while (true) {
            String attValue = Normal.EMPTY;

            if (pos == length) {
                return null;
            }

            switch (chars[pos]) {
                case Symbol.C_DOUBLE_QUOTES:
                    attValue = quotedAV();
                    break;
                case Symbol.C_SHAPE:
                    attValue = hexAV();
                    break;
                case Symbol.C_PLUS:
                case Symbol.C_COMMA:
                case Symbol.C_SEMICOLON:
                    break;
                default:
                    attValue = escapedAV();
            }

            // Values are ordered from most specific to least specific
            // due to the RFC2253 formatting. So take the first match
            // we see.
            if (attributeType.equalsIgnoreCase(attType)) {
                return attValue;
            }

            if (pos >= length) {
                return null;
            }

            if (chars[pos] == Symbol.C_COMMA || chars[pos] == Symbol.C_SEMICOLON) {
            } else if (chars[pos] != Symbol.C_PLUS) {
                throw new IllegalStateException("Malformed DN: " + dn);
            }

            pos++;
            attType = nextAT();
            if (null == attType) {
                throw new IllegalStateException("Malformed DN: " + dn);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy