org.bouncycastle.asn1.x500.style.IETFUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.bouncycastle.asn1.x500.style.IETFUtils.java

Source

package org.bouncycastle.asn1.x500.style;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.DERUniversalString;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.X500NameStyle;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;

public class IETFUtils {
    private static String unescape(String elt) {
        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0)) {
            return elt.trim();
        }

        char[] elts = elt.toCharArray();
        boolean escaped = false;
        boolean quoted = false;
        StringBuffer buf = new StringBuffer(elt.length());
        int start = 0;

        // if it's an escaped hash string and not an actual encoding in string form
        // we need to leave it escaped.
        if (elts[0] == '\\') {
            if (elts[1] == '#') {
                start = 2;
                buf.append("\\#");
            }
        }

        boolean nonWhiteSpaceEncountered = false;
        int lastEscaped = 0;
        char hex1 = 0;

        for (int i = start; i != elts.length; i++) {
            char c = elts[i];

            if (c != ' ') {
                nonWhiteSpaceEncountered = true;
            }

            if (c == '"') {
                if (!escaped) {
                    quoted = !quoted;
                } else {
                    buf.append(c);
                }
                escaped = false;
            } else if (c == '\\' && !(escaped || quoted)) {
                escaped = true;
                lastEscaped = buf.length();
            } else {
                if (c == ' ' && !escaped && !nonWhiteSpaceEncountered) {
                    continue;
                }
                if (escaped && isHexDigit(c)) {
                    if (hex1 != 0) {
                        buf.append((char) (convertHex(hex1) * 16 + convertHex(c)));
                        escaped = false;
                        hex1 = 0;
                        continue;
                    }
                    hex1 = c;
                    continue;
                }
                buf.append(c);
                escaped = false;
            }
        }

        if (buf.length() > 0) {
            while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1)) {
                buf.setLength(buf.length() - 1);
            }
        }

        return buf.toString();
    }

    private static boolean isHexDigit(char c) {
        return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
    }

    private static int convertHex(char c) {
        if ('0' <= c && c <= '9') {
            return c - '0';
        }
        if ('a' <= c && c <= 'f') {
            return c - 'a' + 10;
        }
        return c - 'A' + 10;
    }

    public static RDN[] rDNsFromString(String name, X500NameStyle x500Style) {
        X500NameTokenizer nTok = new X500NameTokenizer(name);
        X500NameBuilder builder = new X500NameBuilder(x500Style);

        while (nTok.hasMoreTokens()) {
            String token = nTok.nextToken();

            if (token.indexOf('+') > 0) {
                X500NameTokenizer pTok = new X500NameTokenizer(token, '+');
                X500NameTokenizer vTok = new X500NameTokenizer(pTok.nextToken(), '=');

                String attr = vTok.nextToken();

                if (!vTok.hasMoreTokens()) {
                    throw new IllegalArgumentException("badly formatted directory string");
                }

                String value = vTok.nextToken();
                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());

                if (pTok.hasMoreTokens()) {
                    Vector oids = new Vector();
                    Vector values = new Vector();

                    oids.addElement(oid);
                    values.addElement(unescape(value));

                    while (pTok.hasMoreTokens()) {
                        vTok = new X500NameTokenizer(pTok.nextToken(), '=');

                        attr = vTok.nextToken();

                        if (!vTok.hasMoreTokens()) {
                            throw new IllegalArgumentException("badly formatted directory string");
                        }

                        value = vTok.nextToken();
                        oid = x500Style.attrNameToOID(attr.trim());

                        oids.addElement(oid);
                        values.addElement(unescape(value));
                    }

                    builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
                } else {
                    builder.addRDN(oid, unescape(value));
                }
            } else {
                X500NameTokenizer vTok = new X500NameTokenizer(token, '=');

                String attr = vTok.nextToken();

                if (!vTok.hasMoreTokens()) {
                    throw new IllegalArgumentException("badly formatted directory string");
                }

                String value = vTok.nextToken();
                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());

                builder.addRDN(oid, unescape(value));
            }
        }

        return builder.build().getRDNs();
    }

    private static String[] toValueArray(Vector values) {
        String[] tmp = new String[values.size()];

        for (int i = 0; i != tmp.length; i++) {
            tmp[i] = (String) values.elementAt(i);
        }

        return tmp;
    }

    private static ASN1ObjectIdentifier[] toOIDArray(Vector oids) {
        ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()];

        for (int i = 0; i != tmp.length; i++) {
            tmp[i] = (ASN1ObjectIdentifier) oids.elementAt(i);
        }

        return tmp;
    }

    public static String[] findAttrNamesForOID(ASN1ObjectIdentifier oid, Hashtable lookup) {
        int count = 0;
        for (Enumeration en = lookup.elements(); en.hasMoreElements();) {
            if (oid.equals(en.nextElement())) {
                count++;
            }
        }

        String[] aliases = new String[count];
        count = 0;

        for (Enumeration en = lookup.keys(); en.hasMoreElements();) {
            String key = (String) en.nextElement();
            if (oid.equals(lookup.get(key))) {
                aliases[count++] = key;
            }
        }

        return aliases;
    }

    public static ASN1ObjectIdentifier decodeAttrName(String name, Hashtable lookUp) {
        if (Strings.toUpperCase(name).startsWith("OID.")) {
            return new ASN1ObjectIdentifier(name.substring(4));
        } else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
            return new ASN1ObjectIdentifier(name);
        }

        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) lookUp.get(Strings.toLowerCase(name));
        if (oid == null) {
            throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
        }

        return oid;
    }

    public static ASN1Encodable valueFromHexString(String str, int off) throws IOException {
        byte[] data = new byte[(str.length() - off) / 2];
        for (int index = 0; index != data.length; index++) {
            char left = str.charAt((index * 2) + off);
            char right = str.charAt((index * 2) + off + 1);

            data[index] = (byte) ((convertHex(left) << 4) | convertHex(right));
        }

        return ASN1Primitive.fromByteArray(data);
    }

    public static void appendRDN(StringBuffer buf, RDN rdn, Hashtable oidSymbols) {
        if (rdn.isMultiValued()) {
            AttributeTypeAndValue[] atv = rdn.getTypesAndValues();
            boolean firstAtv = true;

            for (int j = 0; j != atv.length; j++) {
                if (firstAtv) {
                    firstAtv = false;
                } else {
                    buf.append('+');
                }

                IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols);
            }
        } else {
            if (rdn.getFirst() != null) {
                IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols);
            }
        }
    }

    public static void appendTypeAndValue(StringBuffer buf, AttributeTypeAndValue typeAndValue,
            Hashtable oidSymbols) {
        String sym = (String) oidSymbols.get(typeAndValue.getType());

        if (sym != null) {
            buf.append(sym);
        } else {
            buf.append(typeAndValue.getType().getId());
        }

        buf.append('=');

        buf.append(valueToString(typeAndValue.getValue()));
    }

    public static String valueToString(ASN1Encodable value) {
        StringBuffer vBuf = new StringBuffer();

        if (value instanceof ASN1String && !(value instanceof DERUniversalString)) {
            String v = ((ASN1String) value).getString();
            if (v.length() > 0 && v.charAt(0) == '#') {
                vBuf.append('\\');
            }

            vBuf.append(v);
        } else {
            try {
                vBuf.append('#');
                vBuf.append(Hex.toHexString(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
            } catch (IOException e) {
                throw new IllegalArgumentException("Other value has no encoded form");
            }
        }

        int end = vBuf.length();
        int index = 0;

        if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#') {
            index += 2;
        }

        while (index != end) {
            switch (vBuf.charAt(index)) {
            case ',':
            case '"':
            case '\\':
            case '+':
            case '=':
            case '<':
            case '>':
            case ';': {
                vBuf.insert(index, "\\");
                index += 2;
                ++end;
                break;
            }
            default: {
                ++index;
                break;
            }
            }
        }

        int start = 0;
        if (vBuf.length() > 0) {
            while (vBuf.length() > start && vBuf.charAt(start) == ' ') {
                vBuf.insert(start, "\\");
                start += 2;
            }
        }

        int endBuf = vBuf.length() - 1;

        while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ') {
            vBuf.insert(endBuf, '\\');
            endBuf--;
        }

        return vBuf.toString();
    }

    public static String canonicalize(String s) {
        if (s.length() > 0 && s.charAt(0) == '#') {
            ASN1Primitive obj = decodeObject(s);
            if (obj instanceof ASN1String) {
                s = ((ASN1String) obj).getString();
            }
        }

        s = Strings.toLowerCase(s);

        int length = s.length();
        if (length < 2) {
            return s;
        }

        int start = 0, last = length - 1;
        while (start < last && s.charAt(start) == '\\' && s.charAt(start + 1) == ' ') {
            start += 2;
        }

        int end = last, first = start + 1;
        while (end > first && s.charAt(end - 1) == '\\' && s.charAt(end) == ' ') {
            end -= 2;
        }

        if (start > 0 || end < last) {
            s = s.substring(start, end + 1);
        }

        return stripInternalSpaces(s);
    }

    public static String canonicalString(ASN1Encodable value) {
        return canonicalize(valueToString(value));
    }

    private static ASN1Primitive decodeObject(String oValue) {
        try {
            return ASN1Primitive.fromByteArray(Hex.decodeStrict(oValue, 1, oValue.length() - 1));
        } catch (IOException e) {
            throw new IllegalStateException("unknown encoding in name: " + e);
        }
    }

    public static String stripInternalSpaces(String str) {
        if (str.indexOf("  ") < 0) {
            return str;
        }

        StringBuffer res = new StringBuffer();

        char c1 = str.charAt(0);
        res.append(c1);

        for (int k = 1; k < str.length(); k++) {
            char c2 = str.charAt(k);
            if (!(c1 == ' ' && c2 == ' ')) {
                res.append(c2);
                c1 = c2;
            }
        }

        return res.toString();
    }

    public static boolean rDNAreEqual(RDN rdn1, RDN rdn2) {
        if (rdn1.size() != rdn2.size()) {
            return false;
        }

        AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues();
        AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues();

        if (atvs1.length != atvs2.length) {
            return false;
        }

        for (int i = 0; i != atvs1.length; i++) {
            if (!atvAreEqual(atvs1[i], atvs2[i])) {
                return false;
            }
        }

        return true;
    }

    private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2) {
        if (atv1 == atv2) {
            return true;
        }

        if (null == atv1 || null == atv2) {
            return false;
        }

        ASN1ObjectIdentifier o1 = atv1.getType();
        ASN1ObjectIdentifier o2 = atv2.getType();

        if (!o1.equals(o2)) {
            return false;
        }

        String v1 = canonicalString(atv1.getValue());
        String v2 = canonicalString(atv2.getValue());

        if (!v1.equals(v2)) {
            return false;
        }

        return true;
    }
}