IETFUtils.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.asn1.x500.style;
2
3import java.io.IOException;
4import java.util.Hashtable;
5import java.util.Vector;
6
7import org.bouncycastle.asn1.ASN1Encodable;
8import org.bouncycastle.asn1.ASN1Encoding;
9import org.bouncycastle.asn1.ASN1ObjectIdentifier;
10import org.bouncycastle.asn1.ASN1Primitive;
11import org.bouncycastle.asn1.ASN1String;
12import org.bouncycastle.asn1.DERUniversalString;
13import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
14import org.bouncycastle.asn1.x500.RDN;
15import org.bouncycastle.asn1.x500.X500NameBuilder;
16import org.bouncycastle.asn1.x500.X500NameStyle;
17import org.bouncycastle.util.Strings;
18import org.bouncycastle.util.encoders.Hex;
19
20public class IETFUtils
21{
22    public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
23    {
24        X500NameTokenizer nTok = new X500NameTokenizer(name);
25        X500NameBuilder builder = new X500NameBuilder(x500Style);
26
27        while (nTok.hasMoreTokens())
28        {
29            String  token = nTok.nextToken();
30            int     index = token.indexOf('=');
31
32            if (index == -1)
33            {
34                throw new IllegalArgumentException("badly formated directory string");
35            }
36
37            String               attr = token.substring(0, index);
38            String               value = token.substring(index + 1);
39            ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr);
40
41            if (value.indexOf('+') > 0)
42            {
43                X500NameTokenizer   vTok = new X500NameTokenizer(value, '+');
44                String  v = vTok.nextToken();
45
46                Vector oids = new Vector();
47                Vector values = new Vector();
48
49                oids.addElement(oid);
50                values.addElement(v);
51
52                while (vTok.hasMoreTokens())
53                {
54                    String  sv = vTok.nextToken();
55                    int     ndx = sv.indexOf('=');
56
57                    String  nm = sv.substring(0, ndx);
58                    String  vl = sv.substring(ndx + 1);
59
60                    oids.addElement(x500Style.attrNameToOID(nm));
61                    values.addElement(vl);
62                }
63
64                builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
65            }
66            else
67            {
68                builder.addRDN(oid, value);
69            }
70        }
71
72        return builder.build().getRDNs();
73    }
74
75    private static String[] toValueArray(Vector values)
76    {
77        String[] tmp = new String[values.size()];
78
79        for (int i = 0; i != tmp.length; i++)
80        {
81            tmp[i] = (String)values.elementAt(i);
82        }
83
84        return tmp;
85    }
86
87    private static ASN1ObjectIdentifier[] toOIDArray(Vector oids)
88    {
89        ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()];
90
91        for (int i = 0; i != tmp.length; i++)
92        {
93            tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i);
94        }
95
96        return tmp;
97    }
98
99    public static ASN1ObjectIdentifier decodeAttrName(
100        String      name,
101        Hashtable   lookUp)
102    {
103        if (Strings.toUpperCase(name).startsWith("OID."))
104        {
105            return new ASN1ObjectIdentifier(name.substring(4));
106        }
107        else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
108        {
109            return new ASN1ObjectIdentifier(name);
110        }
111
112        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
113        if (oid == null)
114        {
115            throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
116        }
117
118        return oid;
119    }
120
121    public static ASN1Encodable valueFromHexString(
122        String  str,
123        int     off)
124        throws IOException
125    {
126        str = Strings.toLowerCase(str);
127        byte[] data = new byte[(str.length() - off) / 2];
128        for (int index = 0; index != data.length; index++)
129        {
130            char left = str.charAt((index * 2) + off);
131            char right = str.charAt((index * 2) + off + 1);
132
133            if (left < 'a')
134            {
135                data[index] = (byte)((left - '0') << 4);
136            }
137            else
138            {
139                data[index] = (byte)((left - 'a' + 10) << 4);
140            }
141            if (right < 'a')
142            {
143                data[index] |= (byte)(right - '0');
144            }
145            else
146            {
147                data[index] |= (byte)(right - 'a' + 10);
148            }
149        }
150
151        return ASN1Primitive.fromByteArray(data);
152    }
153
154    public static void appendTypeAndValue(
155        StringBuffer          buf,
156        AttributeTypeAndValue typeAndValue,
157        Hashtable             oidSymbols)
158    {
159        String  sym = (String)oidSymbols.get(typeAndValue.getType());
160
161        if (sym != null)
162        {
163            buf.append(sym);
164        }
165        else
166        {
167            buf.append(typeAndValue.getType().getId());
168        }
169
170        buf.append('=');
171
172        buf.append(valueToString(typeAndValue.getValue()));
173    }
174
175    public static String valueToString(ASN1Encodable value)
176    {
177        StringBuffer vBuf = new StringBuffer();
178
179        if (value instanceof ASN1String && !(value instanceof DERUniversalString))
180        {
181            String v = ((ASN1String)value).getString();
182            if (v.length() > 0 && v.charAt(0) == '#')
183            {
184                vBuf.append("\\" + v);
185            }
186            else
187            {
188                vBuf.append(v);
189            }
190        }
191        else
192        {
193            try
194            {
195                vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
196            }
197            catch (IOException e)
198            {
199                throw new IllegalArgumentException("Other value has no encoded form");
200            }
201        }
202
203        int     end = vBuf.length();
204        int     index = 0;
205
206        if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#')
207        {
208            index += 2;
209        }
210
211        while (index != end)
212        {
213            if ((vBuf.charAt(index) == ',')
214               || (vBuf.charAt(index) == '"')
215               || (vBuf.charAt(index) == '\\')
216               || (vBuf.charAt(index) == '+')
217               || (vBuf.charAt(index) == '=')
218               || (vBuf.charAt(index) == '<')
219               || (vBuf.charAt(index) == '>')
220               || (vBuf.charAt(index) == ';'))
221            {
222                vBuf.insert(index, "\\");
223                index++;
224                end++;
225            }
226
227            index++;
228        }
229
230        return vBuf.toString();
231    }
232
233    private static String bytesToString(
234        byte[] data)
235    {
236        char[]  cs = new char[data.length];
237
238        for (int i = 0; i != cs.length; i++)
239        {
240            cs[i] = (char)(data[i] & 0xff);
241        }
242
243        return new String(cs);
244    }
245
246    public static String canonicalize(String s)
247    {
248        String value = Strings.toLowerCase(s.trim());
249
250        if (value.length() > 0 && value.charAt(0) == '#')
251        {
252            ASN1Primitive obj = decodeObject(value);
253
254            if (obj instanceof ASN1String)
255            {
256                value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
257            }
258        }
259
260        value = stripInternalSpaces(value);
261
262        return value;
263    }
264
265    private static ASN1Primitive decodeObject(String oValue)
266    {
267        try
268        {
269            return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
270        }
271        catch (IOException e)
272        {
273            throw new IllegalStateException("unknown encoding in name: " + e);
274        }
275    }
276
277    public static String stripInternalSpaces(
278        String str)
279    {
280        StringBuffer res = new StringBuffer();
281
282        if (str.length() != 0)
283        {
284            char c1 = str.charAt(0);
285
286            res.append(c1);
287
288            for (int k = 1; k < str.length(); k++)
289            {
290                char c2 = str.charAt(k);
291                if (!(c1 == ' ' && c2 == ' '))
292                {
293                    res.append(c2);
294                }
295                c1 = c2;
296            }
297        }
298
299        return res.toString();
300    }
301}
302