1package org.bouncycastle.asn1.x509;
2
3import java.io.IOException;
4
5import org.bouncycastle.asn1.ASN1InputStream;
6import org.bouncycastle.asn1.DERObject;
7import org.bouncycastle.asn1.DERObjectIdentifier;
8import org.bouncycastle.util.Strings;
9
10/**
11 * It turns out that the number of standard ways the fields in a DN should be
12 * encoded into their ASN.1 counterparts is rapidly approaching the
13 * number of machines on the internet. By default the X509Name class
14 * will produce UTF8Strings in line with the current recommendations (RFC 3280).
15 * <p>
16 * An example of an encoder look like below:
17 * <pre>
18 * public class X509DirEntryConverter
19 *     extends X509NameEntryConverter
20 * {
21 *     public DERObject getConvertedValue(
22 *         DERObjectIdentifier  oid,
23 *         String               value)
24 *     {
25 *         if (str.length() != 0 && str.charAt(0) == '#')
26 *         {
27 *             return convertHexEncoded(str, 1);
28 *         }
29 *         if (oid.equals(EmailAddress))
30 *         {
31 *             return new DERIA5String(str);
32 *         }
33 *         else if (canBePrintable(str))
34 *         {
35 *             return new DERPrintableString(str);
36 *         }
37 *         else if (canBeUTF8(str))
38 *         {
39 *             return new DERUTF8String(str);
40 *         }
41 *         else
42 *         {
43 *             return new DERBMPString(str);
44 *         }
45 *     }
46 * }
47 */
48public abstract class X509NameEntryConverter
49{
50    /**
51     * Convert an inline encoded hex string rendition of an ASN.1
52     * object back into its corresponding ASN.1 object.
53     *
54     * @param str the hex encoded object
55     * @param off the index at which the encoding starts
56     * @return the decoded object
57     */
58    protected DERObject convertHexEncoded(
59        String  str,
60        int     off)
61        throws IOException
62    {
63        str = Strings.toLowerCase(str);
64        byte[] data = new byte[(str.length() - off) / 2];
65        for (int index = 0; index != data.length; index++)
66        {
67            char left = str.charAt((index * 2) + off);
68            char right = str.charAt((index * 2) + off + 1);
69
70            if (left < 'a')
71            {
72                data[index] = (byte)((left - '0') << 4);
73            }
74            else
75            {
76                data[index] = (byte)((left - 'a' + 10) << 4);
77            }
78            if (right < 'a')
79            {
80                data[index] |= (byte)(right - '0');
81            }
82            else
83            {
84                data[index] |= (byte)(right - 'a' + 10);
85            }
86        }
87
88        ASN1InputStream aIn = new ASN1InputStream(data);
89
90        return aIn.readObject();
91    }
92
93    /**
94     * return true if the passed in String can be represented without
95     * loss as a PrintableString, false otherwise.
96     */
97    protected boolean canBePrintable(
98        String  str)
99    {
100        for (int i = str.length() - 1; i >= 0; i--)
101        {
102            char    ch = str.charAt(i);
103
104            if (str.charAt(i) > 0x007f)
105            {
106                return false;
107            }
108
109            if ('a' <= ch && ch <= 'z')
110            {
111                continue;
112            }
113
114            if ('A' <= ch && ch <= 'Z')
115            {
116                continue;
117            }
118
119            if ('0' <= ch && ch <= '9')
120            {
121                continue;
122            }
123
124            switch (ch)
125            {
126            case ' ':
127            case '\'':
128            case '(':
129            case ')':
130            case '+':
131            case '-':
132            case '.':
133            case ':':
134            case '=':
135            case '?':
136                continue;
137            }
138
139            return false;
140        }
141
142        return true;
143    }
144
145    /**
146     * Convert the passed in String value into the appropriate ASN.1
147     * encoded object.
148     *
149     * @param oid the oid associated with the value in the DN.
150     * @param value the value of the particular DN component.
151     * @return the ASN.1 equivalent for the value.
152     */
153    public abstract DERObject getConvertedValue(DERObjectIdentifier oid, String value);
154}
155