author | weijun |
Tue, 14 Aug 2018 22:39:34 +0800 | |
changeset 51398 | 3c389a284345 |
parent 47216 | 71c04702a3d5 |
child 53018 | 8bf9268df0e2 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
51398
3c389a284345
8209416: Refactoring GetPropertyAction calls in security libs
weijun
parents:
47216
diff
changeset
|
2 |
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.security.x509; |
|
27 |
||
28 |
import java.io.ByteArrayOutputStream; |
|
29 |
import java.io.IOException; |
|
30 |
import java.io.OutputStream; |
|
31 |
import java.io.Reader; |
|
32 |
import java.security.AccessController; |
|
33 |
import java.text.Normalizer; |
|
34 |
import java.util.*; |
|
35 |
||
36 |
import sun.security.action.GetBooleanAction; |
|
37 |
import sun.security.util.*; |
|
38 |
import sun.security.pkcs.PKCS9Attribute; |
|
39 |
||
40 |
||
41 |
/** |
|
42 |
* X.500 Attribute-Value-Assertion (AVA): an attribute, as identified by |
|
43 |
* some attribute ID, has some particular value. Values are as a rule ASN.1 |
|
44 |
* printable strings. A conventional set of type IDs is recognized when |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
45 |
* parsing (and generating) RFC 1779, 2253 or 4514 syntax strings. |
2 | 46 |
* |
47 |
* <P>AVAs are components of X.500 relative names. Think of them as being |
|
48 |
* individual fields of a database record. The attribute ID is how you |
|
49 |
* identify the field, and the value is part of a particular record. |
|
50 |
* <p> |
|
51 |
* Note that instances of this class are immutable. |
|
52 |
* |
|
53 |
* @see X500Name |
|
54 |
* @see RDN |
|
55 |
* |
|
56 |
* |
|
57 |
* @author David Brownell |
|
58 |
* @author Amit Kapoor |
|
59 |
* @author Hemma Prafullchandra |
|
60 |
*/ |
|
61 |
public class AVA implements DerEncoder { |
|
62 |
||
63 |
private static final Debug debug = Debug.getInstance("x509", "\t[AVA]"); |
|
64 |
// See CR 6391482: if enabled this flag preserves the old but incorrect |
|
65 |
// PrintableString encoding for DomainComponent. It may need to be set to |
|
66 |
// avoid breaking preexisting certificates generated with sun.security APIs. |
|
51398
3c389a284345
8209416: Refactoring GetPropertyAction calls in security libs
weijun
parents:
47216
diff
changeset
|
67 |
private static final boolean PRESERVE_OLD_DC_ENCODING = GetBooleanAction |
3c389a284345
8209416: Refactoring GetPropertyAction calls in security libs
weijun
parents:
47216
diff
changeset
|
68 |
.privilegedGetProperty("com.sun.security.preserveOldDCEncoding"); |
2 | 69 |
|
70 |
/** |
|
71 |
* DEFAULT format allows both RFC1779 and RFC2253 syntax and |
|
72 |
* additional keywords. |
|
73 |
*/ |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
31538
diff
changeset
|
74 |
static final int DEFAULT = 1; |
2 | 75 |
/** |
76 |
* RFC1779 specifies format according to RFC1779. |
|
77 |
*/ |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
31538
diff
changeset
|
78 |
static final int RFC1779 = 2; |
2 | 79 |
/** |
80 |
* RFC2253 specifies format according to RFC2253. |
|
81 |
*/ |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
31538
diff
changeset
|
82 |
static final int RFC2253 = 3; |
2 | 83 |
|
84 |
// currently not private, accessed directly from RDN |
|
85 |
final ObjectIdentifier oid; |
|
86 |
final DerValue value; |
|
87 |
||
88 |
/* |
|
89 |
* If the value has any of these characters in it, it must be quoted. |
|
90 |
* Backslash and quote characters must also be individually escaped. |
|
91 |
* Leading and trailing spaces, also multiple internal spaces, also |
|
92 |
* call for quoting the whole string. |
|
93 |
*/ |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
94 |
private static final String specialChars1779 = ",=\n+<>#;\\\""; |
2 | 95 |
|
96 |
/* |
|
97 |
* In RFC2253, if the value has any of these characters in it, it |
|
98 |
* must be quoted by a preceding \. |
|
99 |
*/ |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
100 |
private static final String specialChars2253 = ",=+<>#;\\\""; |
2 | 101 |
|
102 |
/* |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
103 |
* includes special chars from RFC1779 and RFC2253, as well as ' ' from |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
104 |
* RFC 4514. |
2 | 105 |
*/ |
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
106 |
private static final String specialCharsDefault = ",=\n+<>#;\\\" "; |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
107 |
private static final String escapedDefault = ",+<>;\""; |
2 | 108 |
|
109 |
/* |
|
110 |
* Values that aren't printable strings are emitted as BER-encoded |
|
111 |
* hex data. |
|
112 |
*/ |
|
113 |
private static final String hexDigits = "0123456789ABCDEF"; |
|
114 |
||
115 |
public AVA(ObjectIdentifier type, DerValue val) { |
|
116 |
if ((type == null) || (val == null)) { |
|
117 |
throw new NullPointerException(); |
|
118 |
} |
|
119 |
oid = type; |
|
120 |
value = val; |
|
121 |
} |
|
122 |
||
123 |
/** |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
124 |
* Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum |
2 | 125 |
* or perhaps with quotes. Not all defined AVA tags are supported; |
126 |
* of current note are X.400 related ones (PRMD, ADMD, etc). |
|
127 |
* |
|
128 |
* This terminates at unescaped AVA separators ("+") or RDN |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
129 |
* separators (",", ";"), and removes cosmetic whitespace at the end of |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
130 |
* values. |
2 | 131 |
*/ |
132 |
AVA(Reader in) throws IOException { |
|
133 |
this(in, DEFAULT); |
|
134 |
} |
|
135 |
||
136 |
/** |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
137 |
* Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum |
2 | 138 |
* or perhaps with quotes. Additional keywords can be specified in the |
139 |
* keyword/OID map. |
|
140 |
* |
|
141 |
* This terminates at unescaped AVA separators ("+") or RDN |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
142 |
* separators (",", ";"), and removes cosmetic whitespace at the end of |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
143 |
* values. |
2 | 144 |
*/ |
145 |
AVA(Reader in, Map<String, String> keywordMap) throws IOException { |
|
146 |
this(in, DEFAULT, keywordMap); |
|
147 |
} |
|
148 |
||
149 |
/** |
|
150 |
* Parse an AVA string formatted according to format. |
|
151 |
*/ |
|
152 |
AVA(Reader in, int format) throws IOException { |
|
153 |
this(in, format, Collections.<String, String>emptyMap()); |
|
154 |
} |
|
155 |
||
156 |
/** |
|
157 |
* Parse an AVA string formatted according to format. |
|
158 |
* |
|
159 |
* @param in Reader containing AVA String |
|
160 |
* @param format parsing format |
|
161 |
* @param keywordMap a Map where a keyword String maps to a corresponding |
|
162 |
* OID String. Each AVA keyword will be mapped to the corresponding OID. |
|
163 |
* If an entry does not exist, it will fallback to the builtin |
|
164 |
* keyword/OID mapping. |
|
165 |
* @throws IOException if the AVA String is not valid in the specified |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
166 |
* format or an OID String from the keywordMap is improperly formatted |
2 | 167 |
*/ |
168 |
AVA(Reader in, int format, Map<String, String> keywordMap) |
|
169 |
throws IOException { |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
170 |
// assume format is one of DEFAULT or RFC2253 |
2 | 171 |
|
172 |
StringBuilder temp = new StringBuilder(); |
|
173 |
int c; |
|
174 |
||
175 |
/* |
|
176 |
* First get the keyword indicating the attribute's type, |
|
177 |
* and map it to the appropriate OID. |
|
178 |
*/ |
|
179 |
while (true) { |
|
180 |
c = readChar(in, "Incorrect AVA format"); |
|
181 |
if (c == '=') { |
|
182 |
break; |
|
183 |
} |
|
184 |
temp.append((char)c); |
|
185 |
} |
|
186 |
||
187 |
oid = AVAKeyword.getOID(temp.toString(), format, keywordMap); |
|
188 |
||
189 |
/* |
|
190 |
* Now parse the value. "#hex", a quoted string, or a string |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
191 |
* terminated by "+", ",", ";". Whitespace before or after |
2 | 192 |
* the value is stripped away unless format is RFC2253. |
193 |
*/ |
|
194 |
temp.setLength(0); |
|
195 |
if (format == RFC2253) { |
|
196 |
// read next character |
|
197 |
c = in.read(); |
|
198 |
if (c == ' ') { |
|
199 |
throw new IOException("Incorrect AVA RFC2253 format - " + |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
200 |
"leading space must be escaped"); |
2 | 201 |
} |
202 |
} else { |
|
203 |
// read next character skipping whitespace |
|
204 |
do { |
|
205 |
c = in.read(); |
|
206 |
} while ((c == ' ') || (c == '\n')); |
|
207 |
} |
|
208 |
if (c == -1) { |
|
209 |
// empty value |
|
210 |
value = new DerValue(""); |
|
211 |
return; |
|
212 |
} |
|
213 |
||
214 |
if (c == '#') { |
|
215 |
value = parseHexString(in, format); |
|
216 |
} else if ((c == '"') && (format != RFC2253)) { |
|
217 |
value = parseQuotedString(in, temp); |
|
218 |
} else { |
|
219 |
value = parseString(in, c, format, temp); |
|
220 |
} |
|
221 |
} |
|
222 |
||
223 |
/** |
|
224 |
* Get the ObjectIdentifier of this AVA. |
|
225 |
*/ |
|
226 |
public ObjectIdentifier getObjectIdentifier() { |
|
227 |
return oid; |
|
228 |
} |
|
229 |
||
230 |
/** |
|
231 |
* Get the value of this AVA as a DerValue. |
|
232 |
*/ |
|
233 |
public DerValue getDerValue() { |
|
234 |
return value; |
|
235 |
} |
|
236 |
||
237 |
/** |
|
238 |
* Get the value of this AVA as a String. |
|
239 |
* |
|
240 |
* @exception RuntimeException if we could not obtain the string form |
|
241 |
* (should not occur) |
|
242 |
*/ |
|
243 |
public String getValueString() { |
|
244 |
try { |
|
245 |
String s = value.getAsString(); |
|
246 |
if (s == null) { |
|
247 |
throw new RuntimeException("AVA string is null"); |
|
248 |
} |
|
249 |
return s; |
|
250 |
} catch (IOException e) { |
|
251 |
// should not occur |
|
252 |
throw new RuntimeException("AVA error: " + e, e); |
|
253 |
} |
|
254 |
} |
|
255 |
||
256 |
private static DerValue parseHexString |
|
257 |
(Reader in, int format) throws IOException { |
|
258 |
||
259 |
int c; |
|
260 |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
261 |
byte b = 0; |
|
262 |
int cNdx = 0; |
|
263 |
while (true) { |
|
264 |
c = in.read(); |
|
265 |
||
266 |
if (isTerminator(c, format)) { |
|
267 |
break; |
|
268 |
} |
|
269 |
||
270 |
int cVal = hexDigits.indexOf(Character.toUpperCase((char)c)); |
|
271 |
||
272 |
if (cVal == -1) { |
|
273 |
throw new IOException("AVA parse, invalid hex " + |
|
274 |
"digit: "+ (char)c); |
|
275 |
} |
|
276 |
||
277 |
if ((cNdx % 2) == 1) { |
|
278 |
b = (byte)((b * 16) + (byte)(cVal)); |
|
279 |
baos.write(b); |
|
280 |
} else { |
|
281 |
b = (byte)(cVal); |
|
282 |
} |
|
283 |
cNdx++; |
|
284 |
} |
|
285 |
||
286 |
// throw exception if no hex digits |
|
287 |
if (cNdx == 0) { |
|
288 |
throw new IOException("AVA parse, zero hex digits"); |
|
289 |
} |
|
290 |
||
291 |
// throw exception if odd number of hex digits |
|
292 |
if (cNdx % 2 == 1) { |
|
293 |
throw new IOException("AVA parse, odd number of hex digits"); |
|
294 |
} |
|
295 |
||
296 |
return new DerValue(baos.toByteArray()); |
|
297 |
} |
|
298 |
||
299 |
private DerValue parseQuotedString |
|
300 |
(Reader in, StringBuilder temp) throws IOException { |
|
301 |
||
302 |
// RFC1779 specifies that an entire RDN may be enclosed in double |
|
303 |
// quotes. In this case the syntax is any sequence of |
|
304 |
// backslash-specialChar, backslash-backslash, |
|
305 |
// backslash-doublequote, or character other than backslash or |
|
306 |
// doublequote. |
|
307 |
int c = readChar(in, "Quoted string did not end in quote"); |
|
308 |
||
30033
b9c86c17164a
8078468: Update security libraries to use diamond with anonymous classes
darcy
parents:
25859
diff
changeset
|
309 |
List<Byte> embeddedHex = new ArrayList<>(); |
2 | 310 |
boolean isPrintableString = true; |
311 |
while (c != '"') { |
|
312 |
if (c == '\\') { |
|
313 |
c = readChar(in, "Quoted string did not end in quote"); |
|
314 |
||
315 |
// check for embedded hex pairs |
|
316 |
Byte hexByte = null; |
|
317 |
if ((hexByte = getEmbeddedHexPair(c, in)) != null) { |
|
318 |
||
319 |
// always encode AVAs with embedded hex as UTF8 |
|
320 |
isPrintableString = false; |
|
321 |
||
322 |
// append consecutive embedded hex |
|
323 |
// as single string later |
|
324 |
embeddedHex.add(hexByte); |
|
325 |
c = in.read(); |
|
326 |
continue; |
|
327 |
} |
|
328 |
||
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
329 |
if (specialChars1779.indexOf((char)c) < 0) { |
2 | 330 |
throw new IOException |
331 |
("Invalid escaped character in AVA: " + |
|
332 |
(char)c); |
|
333 |
} |
|
334 |
} |
|
335 |
||
336 |
// add embedded hex bytes before next char |
|
337 |
if (embeddedHex.size() > 0) { |
|
338 |
String hexString = getEmbeddedHexString(embeddedHex); |
|
339 |
temp.append(hexString); |
|
340 |
embeddedHex.clear(); |
|
341 |
} |
|
342 |
||
343 |
// check for non-PrintableString chars |
|
344 |
isPrintableString &= DerValue.isPrintableStringChar((char)c); |
|
345 |
temp.append((char)c); |
|
346 |
c = readChar(in, "Quoted string did not end in quote"); |
|
347 |
} |
|
348 |
||
349 |
// add trailing embedded hex bytes |
|
350 |
if (embeddedHex.size() > 0) { |
|
351 |
String hexString = getEmbeddedHexString(embeddedHex); |
|
352 |
temp.append(hexString); |
|
353 |
embeddedHex.clear(); |
|
354 |
} |
|
355 |
||
356 |
do { |
|
357 |
c = in.read(); |
|
358 |
} while ((c == '\n') || (c == ' ')); |
|
359 |
if (c != -1) { |
|
360 |
throw new IOException("AVA had characters other than " |
|
361 |
+ "whitespace after terminating quote"); |
|
362 |
} |
|
363 |
||
364 |
// encode as PrintableString unless value contains |
|
365 |
// non-PrintableString chars |
|
31426
9cd672654f97
8022444: Remove sun.security.util.ObjectIdentifier.equals(ObjectIdentifier other) method
juh
parents:
30649
diff
changeset
|
366 |
if (this.oid.equals(PKCS9Attribute.EMAIL_ADDRESS_OID) || |
9cd672654f97
8022444: Remove sun.security.util.ObjectIdentifier.equals(ObjectIdentifier other) method
juh
parents:
30649
diff
changeset
|
367 |
(this.oid.equals(X500Name.DOMAIN_COMPONENT_OID) && |
2 | 368 |
PRESERVE_OLD_DC_ENCODING == false)) { |
369 |
// EmailAddress and DomainComponent must be IA5String |
|
370 |
return new DerValue(DerValue.tag_IA5String, |
|
371 |
temp.toString().trim()); |
|
372 |
} else if (isPrintableString) { |
|
373 |
return new DerValue(temp.toString().trim()); |
|
374 |
} else { |
|
375 |
return new DerValue(DerValue.tag_UTF8String, |
|
376 |
temp.toString().trim()); |
|
377 |
} |
|
378 |
} |
|
379 |
||
380 |
private DerValue parseString |
|
381 |
(Reader in, int c, int format, StringBuilder temp) throws IOException { |
|
382 |
||
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
383 |
List<Byte> embeddedHex = new ArrayList<>(); |
2 | 384 |
boolean isPrintableString = true; |
385 |
boolean escape = false; |
|
386 |
boolean leadingChar = true; |
|
387 |
int spaceCount = 0; |
|
388 |
do { |
|
389 |
escape = false; |
|
390 |
if (c == '\\') { |
|
391 |
escape = true; |
|
392 |
c = readChar(in, "Invalid trailing backslash"); |
|
393 |
||
394 |
// check for embedded hex pairs |
|
395 |
Byte hexByte = null; |
|
396 |
if ((hexByte = getEmbeddedHexPair(c, in)) != null) { |
|
397 |
||
398 |
// always encode AVAs with embedded hex as UTF8 |
|
399 |
isPrintableString = false; |
|
400 |
||
401 |
// append consecutive embedded hex |
|
402 |
// as single string later |
|
403 |
embeddedHex.add(hexByte); |
|
404 |
c = in.read(); |
|
405 |
leadingChar = false; |
|
406 |
continue; |
|
407 |
} |
|
408 |
||
409 |
// check if character was improperly escaped |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
410 |
if (format == DEFAULT && |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
411 |
specialCharsDefault.indexOf((char)c) == -1) { |
2 | 412 |
throw new IOException |
413 |
("Invalid escaped character in AVA: '" + |
|
414 |
(char)c + "'"); |
|
415 |
} else if (format == RFC2253) { |
|
416 |
if (c == ' ') { |
|
417 |
// only leading/trailing space can be escaped |
|
418 |
if (!leadingChar && !trailingSpace(in)) { |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
419 |
throw new IOException |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
420 |
("Invalid escaped space character " + |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
421 |
"in AVA. Only a leading or trailing " + |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
422 |
"space character can be escaped."); |
2 | 423 |
} |
424 |
} else if (c == '#') { |
|
425 |
// only leading '#' can be escaped |
|
426 |
if (!leadingChar) { |
|
427 |
throw new IOException |
|
428 |
("Invalid escaped '#' character in AVA. " + |
|
429 |
"Only a leading '#' can be escaped."); |
|
430 |
} |
|
431 |
} else if (specialChars2253.indexOf((char)c) == -1) { |
|
432 |
throw new IOException |
|
433 |
("Invalid escaped character in AVA: '" + |
|
434 |
(char)c + "'"); |
|
435 |
} |
|
436 |
} |
|
437 |
} else { |
|
438 |
// check if character should have been escaped |
|
439 |
if (format == RFC2253) { |
|
440 |
if (specialChars2253.indexOf((char)c) != -1) { |
|
441 |
throw new IOException |
|
442 |
("Character '" + (char)c + |
|
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
443 |
"' in AVA appears without escape"); |
2 | 444 |
} |
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
445 |
} else if (escapedDefault.indexOf((char)c) != -1) { |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
446 |
throw new IOException |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
447 |
("Character '" + (char)c + |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
448 |
"' in AVA appears without escape"); |
2 | 449 |
} |
450 |
} |
|
451 |
||
452 |
// add embedded hex bytes before next char |
|
453 |
if (embeddedHex.size() > 0) { |
|
454 |
// add space(s) before embedded hex bytes |
|
455 |
for (int i = 0; i < spaceCount; i++) { |
|
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
456 |
temp.append(' '); |
2 | 457 |
} |
458 |
spaceCount = 0; |
|
459 |
||
460 |
String hexString = getEmbeddedHexString(embeddedHex); |
|
461 |
temp.append(hexString); |
|
462 |
embeddedHex.clear(); |
|
463 |
} |
|
464 |
||
465 |
// check for non-PrintableString chars |
|
466 |
isPrintableString &= DerValue.isPrintableStringChar((char)c); |
|
467 |
if (c == ' ' && escape == false) { |
|
468 |
// do not add non-escaped spaces yet |
|
469 |
// (non-escaped trailing spaces are ignored) |
|
470 |
spaceCount++; |
|
471 |
} else { |
|
472 |
// add space(s) |
|
473 |
for (int i = 0; i < spaceCount; i++) { |
|
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
474 |
temp.append(' '); |
2 | 475 |
} |
476 |
spaceCount = 0; |
|
477 |
temp.append((char)c); |
|
478 |
} |
|
479 |
c = in.read(); |
|
480 |
leadingChar = false; |
|
481 |
} while (isTerminator(c, format) == false); |
|
482 |
||
483 |
if (format == RFC2253 && spaceCount > 0) { |
|
484 |
throw new IOException("Incorrect AVA RFC2253 format - " + |
|
485 |
"trailing space must be escaped"); |
|
486 |
} |
|
487 |
||
488 |
// add trailing embedded hex bytes |
|
489 |
if (embeddedHex.size() > 0) { |
|
490 |
String hexString = getEmbeddedHexString(embeddedHex); |
|
491 |
temp.append(hexString); |
|
492 |
embeddedHex.clear(); |
|
493 |
} |
|
494 |
||
495 |
// encode as PrintableString unless value contains |
|
496 |
// non-PrintableString chars |
|
31426
9cd672654f97
8022444: Remove sun.security.util.ObjectIdentifier.equals(ObjectIdentifier other) method
juh
parents:
30649
diff
changeset
|
497 |
if (this.oid.equals(PKCS9Attribute.EMAIL_ADDRESS_OID) || |
9cd672654f97
8022444: Remove sun.security.util.ObjectIdentifier.equals(ObjectIdentifier other) method
juh
parents:
30649
diff
changeset
|
498 |
(this.oid.equals(X500Name.DOMAIN_COMPONENT_OID) && |
2 | 499 |
PRESERVE_OLD_DC_ENCODING == false)) { |
500 |
// EmailAddress and DomainComponent must be IA5String |
|
501 |
return new DerValue(DerValue.tag_IA5String, temp.toString()); |
|
502 |
} else if (isPrintableString) { |
|
503 |
return new DerValue(temp.toString()); |
|
504 |
} else { |
|
505 |
return new DerValue(DerValue.tag_UTF8String, temp.toString()); |
|
506 |
} |
|
507 |
} |
|
508 |
||
509 |
private static Byte getEmbeddedHexPair(int c1, Reader in) |
|
510 |
throws IOException { |
|
511 |
||
512 |
if (hexDigits.indexOf(Character.toUpperCase((char)c1)) >= 0) { |
|
513 |
int c2 = readChar(in, "unexpected EOF - " + |
|
514 |
"escaped hex value must include two valid digits"); |
|
515 |
||
516 |
if (hexDigits.indexOf(Character.toUpperCase((char)c2)) >= 0) { |
|
517 |
int hi = Character.digit((char)c1, 16); |
|
518 |
int lo = Character.digit((char)c2, 16); |
|
25187
08aff438def8
8048874: Replace uses of 'new Byte', 'new Short' and 'new Character' with appropriate alternative across core classes
prappo
parents:
10590
diff
changeset
|
519 |
return (byte)((hi<<4) + lo); |
2 | 520 |
} else { |
521 |
throw new IOException |
|
522 |
("escaped hex value must include two valid digits"); |
|
523 |
} |
|
524 |
} |
|
525 |
return null; |
|
526 |
} |
|
527 |
||
528 |
private static String getEmbeddedHexString(List<Byte> hexList) |
|
529 |
throws IOException { |
|
530 |
int n = hexList.size(); |
|
531 |
byte[] hexBytes = new byte[n]; |
|
532 |
for (int i = 0; i < n; i++) { |
|
533 |
hexBytes[i] = hexList.get(i).byteValue(); |
|
534 |
} |
|
535 |
return new String(hexBytes, "UTF8"); |
|
536 |
} |
|
537 |
||
538 |
private static boolean isTerminator(int ch, int format) { |
|
539 |
switch (ch) { |
|
540 |
case -1: |
|
541 |
case '+': |
|
542 |
case ',': |
|
543 |
return true; |
|
544 |
case ';': |
|
545 |
return format != RFC2253; |
|
546 |
default: |
|
547 |
return false; |
|
548 |
} |
|
549 |
} |
|
550 |
||
551 |
private static int readChar(Reader in, String errMsg) throws IOException { |
|
552 |
int c = in.read(); |
|
553 |
if (c == -1) { |
|
554 |
throw new IOException(errMsg); |
|
555 |
} |
|
556 |
return c; |
|
557 |
} |
|
558 |
||
559 |
private static boolean trailingSpace(Reader in) throws IOException { |
|
560 |
||
561 |
boolean trailing = false; |
|
562 |
||
563 |
if (!in.markSupported()) { |
|
564 |
// oh well |
|
565 |
return true; |
|
566 |
} else { |
|
567 |
// make readAheadLimit huge - |
|
568 |
// in practice, AVA was passed a StringReader from X500Name, |
|
569 |
// and StringReader ignores readAheadLimit anyways |
|
570 |
in.mark(9999); |
|
571 |
while (true) { |
|
572 |
int nextChar = in.read(); |
|
573 |
if (nextChar == -1) { |
|
574 |
trailing = true; |
|
575 |
break; |
|
576 |
} else if (nextChar == ' ') { |
|
577 |
continue; |
|
578 |
} else if (nextChar == '\\') { |
|
579 |
int followingChar = in.read(); |
|
580 |
if (followingChar != ' ') { |
|
581 |
trailing = false; |
|
582 |
break; |
|
583 |
} |
|
584 |
} else { |
|
585 |
trailing = false; |
|
586 |
break; |
|
587 |
} |
|
588 |
} |
|
589 |
||
590 |
in.reset(); |
|
591 |
return trailing; |
|
592 |
} |
|
593 |
} |
|
594 |
||
595 |
AVA(DerValue derval) throws IOException { |
|
596 |
// Individual attribute value assertions are SEQUENCE of two values. |
|
597 |
// That'd be a "struct" outside of ASN.1. |
|
598 |
if (derval.tag != DerValue.tag_Sequence) { |
|
599 |
throw new IOException("AVA not a sequence"); |
|
600 |
} |
|
601 |
oid = X500Name.intern(derval.data.getOID()); |
|
602 |
value = derval.data.getDerValue(); |
|
603 |
||
604 |
if (derval.data.available() != 0) { |
|
605 |
throw new IOException("AVA, extra bytes = " |
|
606 |
+ derval.data.available()); |
|
607 |
} |
|
608 |
} |
|
609 |
||
610 |
AVA(DerInputStream in) throws IOException { |
|
611 |
this(in.getDerValue()); |
|
612 |
} |
|
613 |
||
614 |
public boolean equals(Object obj) { |
|
615 |
if (this == obj) { |
|
616 |
return true; |
|
617 |
} |
|
618 |
if (obj instanceof AVA == false) { |
|
619 |
return false; |
|
620 |
} |
|
621 |
AVA other = (AVA)obj; |
|
622 |
return this.toRFC2253CanonicalString().equals |
|
623 |
(other.toRFC2253CanonicalString()); |
|
624 |
} |
|
625 |
||
626 |
/** |
|
627 |
* Returns a hashcode for this AVA. |
|
628 |
* |
|
629 |
* @return a hashcode for this AVA. |
|
630 |
*/ |
|
631 |
public int hashCode() { |
|
632 |
return toRFC2253CanonicalString().hashCode(); |
|
633 |
} |
|
634 |
||
635 |
/* |
|
636 |
* AVAs are encoded as a SEQUENCE of two elements. |
|
637 |
*/ |
|
638 |
public void encode(DerOutputStream out) throws IOException { |
|
639 |
derEncode(out); |
|
640 |
} |
|
641 |
||
642 |
/** |
|
643 |
* DER encode this object onto an output stream. |
|
644 |
* Implements the <code>DerEncoder</code> interface. |
|
645 |
* |
|
646 |
* @param out |
|
647 |
* the output stream on which to write the DER encoding. |
|
648 |
* |
|
649 |
* @exception IOException on encoding error. |
|
650 |
*/ |
|
651 |
public void derEncode(OutputStream out) throws IOException { |
|
652 |
DerOutputStream tmp = new DerOutputStream(); |
|
653 |
DerOutputStream tmp2 = new DerOutputStream(); |
|
654 |
||
655 |
tmp.putOID(oid); |
|
656 |
value.encode(tmp); |
|
657 |
tmp2.write(DerValue.tag_Sequence, tmp); |
|
658 |
out.write(tmp2.toByteArray()); |
|
659 |
} |
|
660 |
||
661 |
private String toKeyword(int format, Map<String, String> oidMap) { |
|
662 |
return AVAKeyword.getKeyword(oid, format, oidMap); |
|
663 |
} |
|
664 |
||
665 |
/** |
|
666 |
* Returns a printable form of this attribute, using RFC 1779 |
|
667 |
* syntax for individual attribute/value assertions. |
|
668 |
*/ |
|
669 |
public String toString() { |
|
670 |
return toKeywordValueString |
|
671 |
(toKeyword(DEFAULT, Collections.<String, String>emptyMap())); |
|
672 |
} |
|
673 |
||
674 |
/** |
|
675 |
* Returns a printable form of this attribute, using RFC 1779 |
|
676 |
* syntax for individual attribute/value assertions. It only |
|
677 |
* emits standardised keywords. |
|
678 |
*/ |
|
679 |
public String toRFC1779String() { |
|
680 |
return toRFC1779String(Collections.<String, String>emptyMap()); |
|
681 |
} |
|
682 |
||
683 |
/** |
|
684 |
* Returns a printable form of this attribute, using RFC 1779 |
|
685 |
* syntax for individual attribute/value assertions. It |
|
686 |
* emits standardised keywords, as well as keywords contained in the |
|
687 |
* OID/keyword map. |
|
688 |
*/ |
|
689 |
public String toRFC1779String(Map<String, String> oidMap) { |
|
690 |
return toKeywordValueString(toKeyword(RFC1779, oidMap)); |
|
691 |
} |
|
692 |
||
693 |
/** |
|
694 |
* Returns a printable form of this attribute, using RFC 2253 |
|
695 |
* syntax for individual attribute/value assertions. It only |
|
696 |
* emits standardised keywords. |
|
697 |
*/ |
|
698 |
public String toRFC2253String() { |
|
699 |
return toRFC2253String(Collections.<String, String>emptyMap()); |
|
700 |
} |
|
701 |
||
702 |
/** |
|
703 |
* Returns a printable form of this attribute, using RFC 2253 |
|
704 |
* syntax for individual attribute/value assertions. It |
|
705 |
* emits standardised keywords, as well as keywords contained in the |
|
706 |
* OID/keyword map. |
|
707 |
*/ |
|
708 |
public String toRFC2253String(Map<String, String> oidMap) { |
|
709 |
/* |
|
710 |
* Section 2.3: The AttributeTypeAndValue is encoded as the string |
|
711 |
* representation of the AttributeType, followed by an equals character |
|
712 |
* ('=' ASCII 61), followed by the string representation of the |
|
713 |
* AttributeValue. The encoding of the AttributeValue is given in |
|
714 |
* section 2.4. |
|
715 |
*/ |
|
716 |
StringBuilder typeAndValue = new StringBuilder(100); |
|
717 |
typeAndValue.append(toKeyword(RFC2253, oidMap)); |
|
718 |
typeAndValue.append('='); |
|
719 |
||
720 |
/* |
|
721 |
* Section 2.4: Converting an AttributeValue from ASN.1 to a String. |
|
722 |
* If the AttributeValue is of a type which does not have a string |
|
723 |
* representation defined for it, then it is simply encoded as an |
|
724 |
* octothorpe character ('#' ASCII 35) followed by the hexadecimal |
|
725 |
* representation of each of the bytes of the BER encoding of the X.500 |
|
726 |
* AttributeValue. This form SHOULD be used if the AttributeType is of |
|
727 |
* the dotted-decimal form. |
|
728 |
*/ |
|
729 |
if ((typeAndValue.charAt(0) >= '0' && typeAndValue.charAt(0) <= '9') || |
|
730 |
!isDerString(value, false)) |
|
731 |
{ |
|
732 |
byte[] data = null; |
|
733 |
try { |
|
734 |
data = value.toByteArray(); |
|
735 |
} catch (IOException ie) { |
|
736 |
throw new IllegalArgumentException("DER Value conversion"); |
|
737 |
} |
|
738 |
typeAndValue.append('#'); |
|
739 |
for (int j = 0; j < data.length; j++) { |
|
740 |
byte b = data[j]; |
|
741 |
typeAndValue.append(Character.forDigit(0xF & (b >>> 4), 16)); |
|
742 |
typeAndValue.append(Character.forDigit(0xF & b, 16)); |
|
743 |
} |
|
744 |
} else { |
|
745 |
/* |
|
746 |
* 2.4 (cont): Otherwise, if the AttributeValue is of a type which |
|
747 |
* has a string representation, the value is converted first to a |
|
748 |
* UTF-8 string according to its syntax specification. |
|
749 |
* |
|
750 |
* NOTE: this implementation only emits DirectoryStrings of the |
|
751 |
* types returned by isDerString(). |
|
752 |
*/ |
|
753 |
String valStr = null; |
|
754 |
try { |
|
755 |
valStr = new String(value.getDataBytes(), "UTF8"); |
|
756 |
} catch (IOException ie) { |
|
757 |
throw new IllegalArgumentException("DER Value conversion"); |
|
758 |
} |
|
759 |
||
760 |
/* |
|
761 |
* 2.4 (cont): If the UTF-8 string does not have any of the |
|
762 |
* following characters which need escaping, then that string can be |
|
763 |
* used as the string representation of the value. |
|
764 |
* |
|
765 |
* o a space or "#" character occurring at the beginning of the |
|
766 |
* string |
|
767 |
* o a space character occurring at the end of the string |
|
768 |
* o one of the characters ",", "+", """, "\", "<", ">" or ";" |
|
769 |
* |
|
770 |
* Implementations MAY escape other characters. |
|
771 |
* |
|
772 |
* NOTE: this implementation also recognizes "=" and "#" as |
|
95
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
773 |
* characters which need escaping, and null which is escaped as |
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
774 |
* '\00' (see RFC 4514). |
2 | 775 |
* |
776 |
* If a character to be escaped is one of the list shown above, then |
|
777 |
* it is prefixed by a backslash ('\' ASCII 92). |
|
778 |
* |
|
779 |
* Otherwise the character to be escaped is replaced by a backslash |
|
780 |
* and two hex digits, which form a single byte in the code of the |
|
781 |
* character. |
|
782 |
*/ |
|
783 |
final String escapees = ",=+<>#;\"\\"; |
|
784 |
StringBuilder sbuffer = new StringBuilder(); |
|
785 |
||
786 |
for (int i = 0; i < valStr.length(); i++) { |
|
787 |
char c = valStr.charAt(i); |
|
788 |
if (DerValue.isPrintableStringChar(c) || |
|
789 |
escapees.indexOf(c) >= 0) { |
|
790 |
||
791 |
// escape escapees |
|
792 |
if (escapees.indexOf(c) >= 0) { |
|
793 |
sbuffer.append('\\'); |
|
794 |
} |
|
795 |
||
796 |
// append printable/escaped char |
|
797 |
sbuffer.append(c); |
|
798 |
||
95
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
799 |
} else if (c == '\u0000') { |
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
800 |
// escape null character |
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
801 |
sbuffer.append("\\00"); |
aa9ad05818b0
6611991: Add support for parsing RFC4514 DNs to X500Principal
mullan
parents:
2
diff
changeset
|
802 |
|
2 | 803 |
} else if (debug != null && Debug.isOn("ava")) { |
804 |
||
805 |
// embed non-printable/non-escaped char |
|
806 |
// as escaped hex pairs for debugging |
|
807 |
byte[] valueBytes = null; |
|
808 |
try { |
|
809 |
valueBytes = Character.toString(c).getBytes("UTF8"); |
|
810 |
} catch (IOException ie) { |
|
811 |
throw new IllegalArgumentException |
|
812 |
("DER Value conversion"); |
|
813 |
} |
|
814 |
for (int j = 0; j < valueBytes.length; j++) { |
|
815 |
sbuffer.append('\\'); |
|
816 |
char hexChar = Character.forDigit |
|
817 |
(0xF & (valueBytes[j] >>> 4), 16); |
|
818 |
sbuffer.append(Character.toUpperCase(hexChar)); |
|
819 |
hexChar = Character.forDigit |
|
820 |
(0xF & (valueBytes[j]), 16); |
|
821 |
sbuffer.append(Character.toUpperCase(hexChar)); |
|
822 |
} |
|
823 |
} else { |
|
824 |
||
825 |
// append non-printable/non-escaped char |
|
826 |
sbuffer.append(c); |
|
827 |
} |
|
828 |
} |
|
829 |
||
830 |
char[] chars = sbuffer.toString().toCharArray(); |
|
831 |
sbuffer = new StringBuilder(); |
|
832 |
||
833 |
// Find leading and trailing whitespace. |
|
834 |
int lead; // index of first char that is not leading whitespace |
|
835 |
for (lead = 0; lead < chars.length; lead++) { |
|
836 |
if (chars[lead] != ' ' && chars[lead] != '\r') { |
|
837 |
break; |
|
838 |
} |
|
839 |
} |
|
840 |
int trail; // index of last char that is not trailing whitespace |
|
841 |
for (trail = chars.length - 1; trail >= 0; trail--) { |
|
842 |
if (chars[trail] != ' ' && chars[trail] != '\r') { |
|
843 |
break; |
|
844 |
} |
|
845 |
} |
|
846 |
||
847 |
// escape leading and trailing whitespace |
|
848 |
for (int i = 0; i < chars.length; i++) { |
|
849 |
char c = chars[i]; |
|
850 |
if (i < lead || i > trail) { |
|
851 |
sbuffer.append('\\'); |
|
852 |
} |
|
853 |
sbuffer.append(c); |
|
854 |
} |
|
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
855 |
typeAndValue.append(sbuffer); |
2 | 856 |
} |
857 |
return typeAndValue.toString(); |
|
858 |
} |
|
859 |
||
860 |
public String toRFC2253CanonicalString() { |
|
861 |
/* |
|
862 |
* Section 2.3: The AttributeTypeAndValue is encoded as the string |
|
863 |
* representation of the AttributeType, followed by an equals character |
|
864 |
* ('=' ASCII 61), followed by the string representation of the |
|
865 |
* AttributeValue. The encoding of the AttributeValue is given in |
|
866 |
* section 2.4. |
|
867 |
*/ |
|
868 |
StringBuilder typeAndValue = new StringBuilder(40); |
|
869 |
typeAndValue.append |
|
870 |
(toKeyword(RFC2253, Collections.<String, String>emptyMap())); |
|
871 |
typeAndValue.append('='); |
|
872 |
||
873 |
/* |
|
874 |
* Section 2.4: Converting an AttributeValue from ASN.1 to a String. |
|
875 |
* If the AttributeValue is of a type which does not have a string |
|
876 |
* representation defined for it, then it is simply encoded as an |
|
877 |
* octothorpe character ('#' ASCII 35) followed by the hexadecimal |
|
878 |
* representation of each of the bytes of the BER encoding of the X.500 |
|
879 |
* AttributeValue. This form SHOULD be used if the AttributeType is of |
|
880 |
* the dotted-decimal form. |
|
881 |
*/ |
|
882 |
if ((typeAndValue.charAt(0) >= '0' && typeAndValue.charAt(0) <= '9') || |
|
883 |
!isDerString(value, true)) |
|
884 |
{ |
|
885 |
byte[] data = null; |
|
886 |
try { |
|
887 |
data = value.toByteArray(); |
|
888 |
} catch (IOException ie) { |
|
889 |
throw new IllegalArgumentException("DER Value conversion"); |
|
890 |
} |
|
891 |
typeAndValue.append('#'); |
|
892 |
for (int j = 0; j < data.length; j++) { |
|
893 |
byte b = data[j]; |
|
894 |
typeAndValue.append(Character.forDigit(0xF & (b >>> 4), 16)); |
|
895 |
typeAndValue.append(Character.forDigit(0xF & b, 16)); |
|
896 |
} |
|
897 |
} else { |
|
898 |
/* |
|
899 |
* 2.4 (cont): Otherwise, if the AttributeValue is of a type which |
|
900 |
* has a string representation, the value is converted first to a |
|
901 |
* UTF-8 string according to its syntax specification. |
|
902 |
* |
|
903 |
* NOTE: this implementation only emits DirectoryStrings of the |
|
904 |
* types returned by isDerString(). |
|
905 |
*/ |
|
906 |
String valStr = null; |
|
907 |
try { |
|
908 |
valStr = new String(value.getDataBytes(), "UTF8"); |
|
909 |
} catch (IOException ie) { |
|
910 |
throw new IllegalArgumentException("DER Value conversion"); |
|
911 |
} |
|
912 |
||
913 |
/* |
|
914 |
* 2.4 (cont): If the UTF-8 string does not have any of the |
|
915 |
* following characters which need escaping, then that string can be |
|
916 |
* used as the string representation of the value. |
|
917 |
* |
|
918 |
* o a space or "#" character occurring at the beginning of the |
|
919 |
* string |
|
920 |
* o a space character occurring at the end of the string |
|
921 |
* |
|
922 |
* o one of the characters ",", "+", """, "\", "<", ">" or ";" |
|
923 |
* |
|
924 |
* If a character to be escaped is one of the list shown above, then |
|
925 |
* it is prefixed by a backslash ('\' ASCII 92). |
|
926 |
* |
|
927 |
* Otherwise the character to be escaped is replaced by a backslash |
|
928 |
* and two hex digits, which form a single byte in the code of the |
|
929 |
* character. |
|
930 |
*/ |
|
931 |
final String escapees = ",+<>;\"\\"; |
|
932 |
StringBuilder sbuffer = new StringBuilder(); |
|
933 |
boolean previousWhite = false; |
|
934 |
||
935 |
for (int i = 0; i < valStr.length(); i++) { |
|
936 |
char c = valStr.charAt(i); |
|
937 |
||
938 |
if (DerValue.isPrintableStringChar(c) || |
|
939 |
escapees.indexOf(c) >= 0 || |
|
940 |
(i == 0 && c == '#')) { |
|
941 |
||
942 |
// escape leading '#' and escapees |
|
943 |
if ((i == 0 && c == '#') || escapees.indexOf(c) >= 0) { |
|
944 |
sbuffer.append('\\'); |
|
945 |
} |
|
946 |
||
947 |
// convert multiple whitespace to single whitespace |
|
948 |
if (!Character.isWhitespace(c)) { |
|
949 |
previousWhite = false; |
|
950 |
sbuffer.append(c); |
|
951 |
} else { |
|
952 |
if (previousWhite == false) { |
|
953 |
// add single whitespace |
|
954 |
previousWhite = true; |
|
955 |
sbuffer.append(c); |
|
956 |
} else { |
|
957 |
// ignore subsequent consecutive whitespace |
|
958 |
continue; |
|
959 |
} |
|
960 |
} |
|
961 |
||
962 |
} else if (debug != null && Debug.isOn("ava")) { |
|
963 |
||
964 |
// embed non-printable/non-escaped char |
|
965 |
// as escaped hex pairs for debugging |
|
966 |
||
967 |
previousWhite = false; |
|
968 |
||
31538
0981099a3e54
8130022: Use Java-style array declarations consistently
igerasim
parents:
31426
diff
changeset
|
969 |
byte[] valueBytes = null; |
2 | 970 |
try { |
971 |
valueBytes = Character.toString(c).getBytes("UTF8"); |
|
972 |
} catch (IOException ie) { |
|
973 |
throw new IllegalArgumentException |
|
974 |
("DER Value conversion"); |
|
975 |
} |
|
976 |
for (int j = 0; j < valueBytes.length; j++) { |
|
977 |
sbuffer.append('\\'); |
|
978 |
sbuffer.append(Character.forDigit |
|
979 |
(0xF & (valueBytes[j] >>> 4), 16)); |
|
980 |
sbuffer.append(Character.forDigit |
|
981 |
(0xF & (valueBytes[j]), 16)); |
|
982 |
} |
|
983 |
} else { |
|
984 |
||
985 |
// append non-printable/non-escaped char |
|
986 |
||
987 |
previousWhite = false; |
|
988 |
sbuffer.append(c); |
|
989 |
} |
|
990 |
} |
|
991 |
||
992 |
// remove leading and trailing whitespace from value |
|
993 |
typeAndValue.append(sbuffer.toString().trim()); |
|
994 |
} |
|
995 |
||
996 |
String canon = typeAndValue.toString(); |
|
997 |
canon = canon.toUpperCase(Locale.US).toLowerCase(Locale.US); |
|
998 |
return Normalizer.normalize(canon, Normalizer.Form.NFKD); |
|
999 |
} |
|
1000 |
||
1001 |
/* |
|
1002 |
* Return true if DerValue can be represented as a String. |
|
1003 |
*/ |
|
1004 |
private static boolean isDerString(DerValue value, boolean canonical) { |
|
1005 |
if (canonical) { |
|
1006 |
switch (value.tag) { |
|
1007 |
case DerValue.tag_PrintableString: |
|
1008 |
case DerValue.tag_UTF8String: |
|
1009 |
return true; |
|
1010 |
default: |
|
1011 |
return false; |
|
1012 |
} |
|
1013 |
} else { |
|
1014 |
switch (value.tag) { |
|
1015 |
case DerValue.tag_PrintableString: |
|
1016 |
case DerValue.tag_T61String: |
|
1017 |
case DerValue.tag_IA5String: |
|
1018 |
case DerValue.tag_GeneralString: |
|
1019 |
case DerValue.tag_BMPString: |
|
1020 |
case DerValue.tag_UTF8String: |
|
1021 |
return true; |
|
1022 |
default: |
|
1023 |
return false; |
|
1024 |
} |
|
1025 |
} |
|
1026 |
} |
|
1027 |
||
1028 |
boolean hasRFC2253Keyword() { |
|
1029 |
return AVAKeyword.hasKeyword(oid, RFC2253); |
|
1030 |
} |
|
1031 |
||
1032 |
private String toKeywordValueString(String keyword) { |
|
1033 |
/* |
|
1034 |
* Construct the value with as little copying and garbage |
|
1035 |
* production as practical. First the keyword (mandatory), |
|
1036 |
* then the equals sign, finally the value. |
|
1037 |
*/ |
|
1038 |
StringBuilder retval = new StringBuilder(40); |
|
1039 |
||
1040 |
retval.append(keyword); |
|
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
1041 |
retval.append('='); |
2 | 1042 |
|
1043 |
try { |
|
1044 |
String valStr = value.getAsString(); |
|
1045 |
||
1046 |
if (valStr == null) { |
|
1047 |
||
1048 |
// rfc1779 specifies that attribute values associated |
|
1049 |
// with non-standard keyword attributes may be represented |
|
1050 |
// using the hex format below. This will be used only |
|
1051 |
// when the value is not a string type |
|
1052 |
||
31538
0981099a3e54
8130022: Use Java-style array declarations consistently
igerasim
parents:
31426
diff
changeset
|
1053 |
byte[] data = value.toByteArray(); |
2 | 1054 |
|
1055 |
retval.append('#'); |
|
1056 |
for (int i = 0; i < data.length; i++) { |
|
1057 |
retval.append(hexDigits.charAt((data [i] >> 4) & 0x0f)); |
|
1058 |
retval.append(hexDigits.charAt(data [i] & 0x0f)); |
|
1059 |
} |
|
1060 |
||
1061 |
} else { |
|
1062 |
||
1063 |
boolean quoteNeeded = false; |
|
1064 |
StringBuilder sbuffer = new StringBuilder(); |
|
1065 |
boolean previousWhite = false; |
|
1066 |
final String escapees = ",+=\n<>#;\\\""; |
|
1067 |
||
1068 |
/* |
|
1069 |
* Special characters (e.g. AVA list separators) cause strings |
|
1070 |
* to need quoting, or at least escaping. So do leading or |
|
1071 |
* trailing spaces, and multiple internal spaces. |
|
1072 |
*/ |
|
10590
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1073 |
int length = valStr.length(); |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1074 |
boolean alreadyQuoted = |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1075 |
(length > 1 && valStr.charAt(0) == '\"' |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1076 |
&& valStr.charAt(length - 1) == '\"'); |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1077 |
|
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1078 |
for (int i = 0; i < length; i++) { |
2 | 1079 |
char c = valStr.charAt(i); |
10590
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1080 |
if (alreadyQuoted && (i == 0 || i == length - 1)) { |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1081 |
sbuffer.append(c); |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1082 |
continue; |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1083 |
} |
2 | 1084 |
if (DerValue.isPrintableStringChar(c) || |
1085 |
escapees.indexOf(c) >= 0) { |
|
1086 |
||
1087 |
// quote if leading whitespace or special chars |
|
1088 |
if (!quoteNeeded && |
|
1089 |
((i == 0 && (c == ' ' || c == '\n')) || |
|
1090 |
escapees.indexOf(c) >= 0)) { |
|
1091 |
quoteNeeded = true; |
|
1092 |
} |
|
1093 |
||
1094 |
// quote if multiple internal whitespace |
|
1095 |
if (!(c == ' ' || c == '\n')) { |
|
1096 |
// escape '"' and '\' |
|
1097 |
if (c == '"' || c == '\\') { |
|
1098 |
sbuffer.append('\\'); |
|
1099 |
} |
|
1100 |
previousWhite = false; |
|
1101 |
} else { |
|
1102 |
if (!quoteNeeded && previousWhite) { |
|
1103 |
quoteNeeded = true; |
|
1104 |
} |
|
1105 |
previousWhite = true; |
|
1106 |
} |
|
1107 |
||
1108 |
sbuffer.append(c); |
|
1109 |
||
1110 |
} else if (debug != null && Debug.isOn("ava")) { |
|
1111 |
||
1112 |
// embed non-printable/non-escaped char |
|
1113 |
// as escaped hex pairs for debugging |
|
1114 |
||
1115 |
previousWhite = false; |
|
1116 |
||
1117 |
// embed escaped hex pairs |
|
1118 |
byte[] valueBytes = |
|
1119 |
Character.toString(c).getBytes("UTF8"); |
|
1120 |
for (int j = 0; j < valueBytes.length; j++) { |
|
1121 |
sbuffer.append('\\'); |
|
1122 |
char hexChar = Character.forDigit |
|
1123 |
(0xF & (valueBytes[j] >>> 4), 16); |
|
1124 |
sbuffer.append(Character.toUpperCase(hexChar)); |
|
1125 |
hexChar = Character.forDigit |
|
1126 |
(0xF & (valueBytes[j]), 16); |
|
1127 |
sbuffer.append(Character.toUpperCase(hexChar)); |
|
1128 |
} |
|
1129 |
} else { |
|
1130 |
||
1131 |
// append non-printable/non-escaped char |
|
1132 |
||
1133 |
previousWhite = false; |
|
1134 |
sbuffer.append(c); |
|
1135 |
} |
|
1136 |
} |
|
1137 |
||
1138 |
// quote if trailing whitespace |
|
1139 |
if (sbuffer.length() > 0) { |
|
1140 |
char trailChar = sbuffer.charAt(sbuffer.length() - 1); |
|
1141 |
if (trailChar == ' ' || trailChar == '\n') { |
|
1142 |
quoteNeeded = true; |
|
1143 |
} |
|
1144 |
} |
|
1145 |
||
1146 |
// Emit the string ... quote it if needed |
|
10590
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1147 |
// if string is already quoted, don't re-quote |
f184672e4617
7049963: DISTINGUISHED NAMES FOR CERT ARE ESCAPED IN JROCKIT 1.6(NOT COMPATIBLE WITH JROC
mbankal
parents:
10370
diff
changeset
|
1148 |
if (!alreadyQuoted && quoteNeeded) { |
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
1149 |
retval.append('\"') |
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
1150 |
.append(sbuffer) |
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
1151 |
.append('\"'); |
2 | 1152 |
} else { |
30649
e7cc8f48f616
8080522: Optimize string operations in java.base/share/classes/sun/security/x509/
igerasim
parents:
30033
diff
changeset
|
1153 |
retval.append(sbuffer); |
2 | 1154 |
} |
1155 |
} |
|
1156 |
} catch (IOException e) { |
|
1157 |
throw new IllegalArgumentException("DER Value conversion"); |
|
1158 |
} |
|
1159 |
||
1160 |
return retval.toString(); |
|
1161 |
} |
|
1162 |
||
1163 |
} |
|
1164 |
||
1165 |
/** |
|
1166 |
* Helper class that allows conversion from String to ObjectIdentifier and |
|
1167 |
* vice versa according to RFC1779, RFC2253, and an augmented version of |
|
1168 |
* those standards. |
|
1169 |
*/ |
|
1170 |
class AVAKeyword { |
|
1171 |
||
1172 |
private static final Map<ObjectIdentifier,AVAKeyword> oidMap; |
|
1173 |
private static final Map<String,AVAKeyword> keywordMap; |
|
1174 |
||
1175 |
private String keyword; |
|
1176 |
private ObjectIdentifier oid; |
|
1177 |
private boolean rfc1779Compliant, rfc2253Compliant; |
|
1178 |
||
1179 |
private AVAKeyword(String keyword, ObjectIdentifier oid, |
|
1180 |
boolean rfc1779Compliant, boolean rfc2253Compliant) { |
|
1181 |
this.keyword = keyword; |
|
1182 |
this.oid = oid; |
|
1183 |
this.rfc1779Compliant = rfc1779Compliant; |
|
1184 |
this.rfc2253Compliant = rfc2253Compliant; |
|
1185 |
||
1186 |
// register it |
|
1187 |
oidMap.put(oid, this); |
|
1188 |
keywordMap.put(keyword, this); |
|
1189 |
} |
|
1190 |
||
1191 |
private boolean isCompliant(int standard) { |
|
1192 |
switch (standard) { |
|
1193 |
case AVA.RFC1779: |
|
1194 |
return rfc1779Compliant; |
|
1195 |
case AVA.RFC2253: |
|
1196 |
return rfc2253Compliant; |
|
1197 |
case AVA.DEFAULT: |
|
1198 |
return true; |
|
1199 |
default: |
|
1200 |
// should not occur, internal error |
|
1201 |
throw new IllegalArgumentException("Invalid standard " + standard); |
|
1202 |
} |
|
1203 |
} |
|
1204 |
||
1205 |
/** |
|
1206 |
* Get an object identifier representing the specified keyword (or |
|
1207 |
* string encoded object identifier) in the given standard. |
|
1208 |
* |
|
1209 |
* @param keywordMap a Map where a keyword String maps to a corresponding |
|
1210 |
* OID String. Each AVA keyword will be mapped to the corresponding OID. |
|
1211 |
* If an entry does not exist, it will fallback to the builtin |
|
1212 |
* keyword/OID mapping. |
|
1213 |
* @throws IOException If the keyword is not valid in the specified standard |
|
1214 |
* or the OID String to which a keyword maps to is improperly formatted. |
|
1215 |
*/ |
|
1216 |
static ObjectIdentifier getOID |
|
1217 |
(String keyword, int standard, Map<String, String> extraKeywordMap) |
|
1218 |
throws IOException { |
|
1219 |
||
6122
16fa7ed7ff1b
6867345: Turkish regional options cause NPE in sun.security.x509.AlgorithmId.algOID
xuelei
parents:
5506
diff
changeset
|
1220 |
keyword = keyword.toUpperCase(Locale.ENGLISH); |
2 | 1221 |
if (standard == AVA.RFC2253) { |
1222 |
if (keyword.startsWith(" ") || keyword.endsWith(" ")) { |
|
1223 |
throw new IOException("Invalid leading or trailing space " + |
|
1224 |
"in keyword \"" + keyword + "\""); |
|
1225 |
} |
|
1226 |
} else { |
|
1227 |
keyword = keyword.trim(); |
|
1228 |
} |
|
1229 |
||
1230 |
// check user-specified keyword map first, then fallback to built-in |
|
1231 |
// map |
|
1232 |
String oidString = extraKeywordMap.get(keyword); |
|
1233 |
if (oidString == null) { |
|
1234 |
AVAKeyword ak = keywordMap.get(keyword); |
|
1235 |
if ((ak != null) && ak.isCompliant(standard)) { |
|
1236 |
return ak.oid; |
|
1237 |
} |
|
1238 |
} else { |
|
1239 |
return new ObjectIdentifier(oidString); |
|
1240 |
} |
|
1241 |
||
10370
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
1242 |
// no keyword found, check if OID string |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
1243 |
if (standard == AVA.DEFAULT && keyword.startsWith("OID.")) { |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
1244 |
keyword = keyword.substring(4); |
5db0cf452a50
7024771: "\\<>" in attribute value part of X500Principal constructor parameter makes strange effect
mullan
parents:
10336
diff
changeset
|
1245 |
} |
2 | 1246 |
|
1247 |
boolean number = false; |
|
1248 |
if (keyword.length() != 0) { |
|
1249 |
char ch = keyword.charAt(0); |
|
1250 |
if ((ch >= '0') && (ch <= '9')) { |
|
1251 |
number = true; |
|
1252 |
} |
|
1253 |
} |
|
1254 |
if (number == false) { |
|
1255 |
throw new IOException("Invalid keyword \"" + keyword + "\""); |
|
1256 |
} |
|
1257 |
return new ObjectIdentifier(keyword); |
|
1258 |
} |
|
1259 |
||
1260 |
/** |
|
1261 |
* Get a keyword for the given ObjectIdentifier according to standard. |
|
1262 |
* If no keyword is available, the ObjectIdentifier is encoded as a |
|
1263 |
* String. |
|
1264 |
*/ |
|
1265 |
static String getKeyword(ObjectIdentifier oid, int standard) { |
|
1266 |
return getKeyword |
|
1267 |
(oid, standard, Collections.<String, String>emptyMap()); |
|
1268 |
} |
|
1269 |
||
1270 |
/** |
|
1271 |
* Get a keyword for the given ObjectIdentifier according to standard. |
|
1272 |
* Checks the extraOidMap for a keyword first, then falls back to the |
|
1273 |
* builtin/default set. If no keyword is available, the ObjectIdentifier |
|
1274 |
* is encoded as a String. |
|
1275 |
*/ |
|
1276 |
static String getKeyword |
|
1277 |
(ObjectIdentifier oid, int standard, Map<String, String> extraOidMap) { |
|
1278 |
||
1279 |
// check extraOidMap first, then fallback to built-in map |
|
1280 |
String oidString = oid.toString(); |
|
1281 |
String keywordString = extraOidMap.get(oidString); |
|
1282 |
if (keywordString == null) { |
|
1283 |
AVAKeyword ak = oidMap.get(oid); |
|
1284 |
if ((ak != null) && ak.isCompliant(standard)) { |
|
1285 |
return ak.keyword; |
|
1286 |
} |
|
1287 |
} else { |
|
1288 |
if (keywordString.length() == 0) { |
|
1289 |
throw new IllegalArgumentException("keyword cannot be empty"); |
|
1290 |
} |
|
1291 |
keywordString = keywordString.trim(); |
|
1292 |
char c = keywordString.charAt(0); |
|
1293 |
if (c < 65 || c > 122 || (c > 90 && c < 97)) { |
|
1294 |
throw new IllegalArgumentException |
|
1295 |
("keyword does not start with letter"); |
|
1296 |
} |
|
1297 |
for (int i=1; i<keywordString.length(); i++) { |
|
1298 |
c = keywordString.charAt(i); |
|
1299 |
if ((c < 65 || c > 122 || (c > 90 && c < 97)) && |
|
1300 |
(c < 48 || c > 57) && c != '_') { |
|
1301 |
throw new IllegalArgumentException |
|
1302 |
("keyword character is not a letter, digit, or underscore"); |
|
1303 |
} |
|
1304 |
} |
|
1305 |
return keywordString; |
|
1306 |
} |
|
1307 |
// no compliant keyword, use OID |
|
1308 |
if (standard == AVA.RFC2253) { |
|
1309 |
return oidString; |
|
1310 |
} else { |
|
1311 |
return "OID." + oidString; |
|
1312 |
} |
|
1313 |
} |
|
1314 |
||
1315 |
/** |
|
1316 |
* Test if oid has an associated keyword in standard. |
|
1317 |
*/ |
|
1318 |
static boolean hasKeyword(ObjectIdentifier oid, int standard) { |
|
1319 |
AVAKeyword ak = oidMap.get(oid); |
|
1320 |
if (ak == null) { |
|
1321 |
return false; |
|
1322 |
} |
|
1323 |
return ak.isCompliant(standard); |
|
1324 |
} |
|
1325 |
||
1326 |
static { |
|
1327 |
oidMap = new HashMap<ObjectIdentifier,AVAKeyword>(); |
|
1328 |
keywordMap = new HashMap<String,AVAKeyword>(); |
|
1329 |
||
1330 |
// NOTE if multiple keywords are available for one OID, order |
|
1331 |
// is significant!! Preferred *LAST*. |
|
1332 |
new AVAKeyword("CN", X500Name.commonName_oid, true, true); |
|
1333 |
new AVAKeyword("C", X500Name.countryName_oid, true, true); |
|
1334 |
new AVAKeyword("L", X500Name.localityName_oid, true, true); |
|
1335 |
new AVAKeyword("S", X500Name.stateName_oid, false, false); |
|
1336 |
new AVAKeyword("ST", X500Name.stateName_oid, true, true); |
|
1337 |
new AVAKeyword("O", X500Name.orgName_oid, true, true); |
|
1338 |
new AVAKeyword("OU", X500Name.orgUnitName_oid, true, true); |
|
1339 |
new AVAKeyword("T", X500Name.title_oid, false, false); |
|
1340 |
new AVAKeyword("IP", X500Name.ipAddress_oid, false, false); |
|
1341 |
new AVAKeyword("STREET", X500Name.streetAddress_oid,true, true); |
|
1342 |
new AVAKeyword("DC", X500Name.DOMAIN_COMPONENT_OID, |
|
1343 |
false, true); |
|
1344 |
new AVAKeyword("DNQUALIFIER", X500Name.DNQUALIFIER_OID, false, false); |
|
1345 |
new AVAKeyword("DNQ", X500Name.DNQUALIFIER_OID, false, false); |
|
1346 |
new AVAKeyword("SURNAME", X500Name.SURNAME_OID, false, false); |
|
1347 |
new AVAKeyword("GIVENNAME", X500Name.GIVENNAME_OID, false, false); |
|
1348 |
new AVAKeyword("INITIALS", X500Name.INITIALS_OID, false, false); |
|
1349 |
new AVAKeyword("GENERATION", X500Name.GENERATIONQUALIFIER_OID, |
|
1350 |
false, false); |
|
1351 |
new AVAKeyword("EMAIL", PKCS9Attribute.EMAIL_ADDRESS_OID, false, false); |
|
1352 |
new AVAKeyword("EMAILADDRESS", PKCS9Attribute.EMAIL_ADDRESS_OID, |
|
1353 |
false, false); |
|
1354 |
new AVAKeyword("UID", X500Name.userid_oid, false, true); |
|
1355 |
new AVAKeyword("SERIALNUMBER", X500Name.SERIALNUMBER_OID, false, false); |
|
1356 |
} |
|
1357 |
} |