1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.geronimo.util.asn1.x509;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.IOException;
23
24 import org.apache.geronimo.util.asn1.ASN1InputStream;
25 import org.apache.geronimo.util.asn1.DERObject;
26 import org.apache.geronimo.util.asn1.DERObjectIdentifier;
27
28 /**
29 * It turns out that the number of standard ways the fields in a DN should be
30 * encoded into their ASN.1 counterparts is rapidly approaching the
31 * number of machines on the internet. By default the X509Name class
32 * will produce PrintableStrings if the field value will decode to that,
33 * next UTF8Strings if the field value will decode to that, and finally BMPStrings
34 * if 16 bit characters are required.
35 * <p>
36 * The way this is done is with a default encoder which is
37 * implemented as follows:
38 * <pre>
39 * public class X509DefaultEntryConverter
40 * extends X509NameEntryConverter
41 * {
42 * public DERObject getConvertedValue(
43 * DERObjectIdentifier oid,
44 * String value)
45 * {
46 * if (str.length() != 0 && str.charAt(0) == '#')
47 * {
48 * return convertHexEncoded(str, 1);
49 * }
50 * if (oid.equals(EmailAddress))
51 * {
52 * return new DERIA5String(str);
53 * }
54 * else if (canBePrintable(str))
55 * {
56 * return new DERPrintableString(str);
57 * }
58 * else if (canBeUTF8(str))
59 * {
60 * return new DERUTF8String(str);
61 * }
62 * else
63 * {
64 * return new DERBMPString(str);
65 * }
66 * }
67 * }
68 */
69 public abstract class X509NameEntryConverter
70 {
71 /**
72 * Convert an inline encoded hex string rendition of an ASN.1
73 * object back into its corresponding ASN.1 object.
74 *
75 * @param str the hex encoded object
76 * @param off the index at which the encoding starts
77 * @return the decoded object
78 */
79 protected DERObject convertHexEncoded(
80 String str,
81 int off)
82 throws IOException
83 {
84 str = str.toLowerCase();
85 byte[] data = new byte[str.length() / 2];
86 for (int index = 0; index != data.length; index++)
87 {
88 char left = str.charAt((index * 2) + off);
89 char right = str.charAt((index * 2) + off + 1);
90
91 if (left < 'a')
92 {
93 data[index] = (byte)((left - '0') << 4);
94 }
95 else
96 {
97 data[index] = (byte)((left - 'a' + 10) << 4);
98 }
99 if (right < 'a')
100 {
101 data[index] |= (byte)(right - '0');
102 }
103 else
104 {
105 data[index] |= (byte)(right - 'a' + 10);
106 }
107 }
108
109 ASN1InputStream aIn = new ASN1InputStream(
110 new ByteArrayInputStream(data));
111
112 return aIn.readObject();
113 }
114
115 /**
116 * return true if the passed in String can be represented without
117 * loss as a PrintableString, false otherwise.
118 */
119 protected boolean canBePrintable(
120 String str)
121 {
122 for (int i = str.length() - 1; i >= 0; i--)
123 {
124 char ch = str.charAt(i);
125
126 if (str.charAt(i) > 0x007f)
127 {
128 return false;
129 }
130
131 if ('a' <= ch && ch <= 'z')
132 {
133 continue;
134 }
135
136 if ('A' <= ch && ch <= 'Z')
137 {
138 continue;
139 }
140
141 if ('0' <= ch && ch <= '9')
142 {
143 continue;
144 }
145
146 switch (ch)
147 {
148 case ' ':
149 case '\'':
150 case '(':
151 case ')':
152 case '+':
153 case '-':
154 case '.':
155 case ':':
156 case '=':
157 case '?':
158 continue;
159 }
160
161 return false;
162 }
163
164 return true;
165 }
166
167 /**
168 * return true if the passed in String can be represented without
169 * loss as a UTF8String, false otherwise.
170 */
171 protected boolean canBeUTF8(
172 String str)
173 {
174 for (int i = str.length() - 1; i >= 0; i--)
175 {
176 if (str.charAt(i) > 0x00ff)
177 {
178 return false;
179 }
180 }
181
182 return true;
183 }
184
185 /**
186 * Convert the passed in String value into the appropriate ASN.1
187 * encoded object.
188 *
189 * @param oid the oid associated with the value in the DN.
190 * @param value the value of the particular DN component.
191 * @return the ASN.1 equivalent for the value.
192 */
193 public abstract DERObject getConvertedValue(DERObjectIdentifier oid, String value);
194 }