1 /*
2  * reserved comment block
3  * DO NOT REMOVE OR ALTER!
4  */
5 /**
6  * Licensed to the Apache Software Foundation (ASF) under one
7  * or more contributor license agreements. See the NOTICE file
8  * distributed with this work for additional information
9  * regarding copyright ownership. The ASF licenses this file
10  * to you under the Apache License, Version 2.0 (the
11  * "License"); you may not use this file except in compliance
12  * with the License. You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing,
17  * software distributed under the License is distributed on an
18  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19  * KIND, either express or implied. See the License for the
20  * specific language governing permissions and limitations
21  * under the License.
22  */
23 package com.sun.org.apache.xml.internal.security.utils;
24 
25 import java.io.IOException;
26 import java.io.StringReader;
27 
28 public class RFC2253Parser {
29 
30     /**
31      * Method rfc2253toXMLdsig
32      *
33      * @param dn
34      * @return normalized string
35      */
rfc2253toXMLdsig(String dn)36     public static String rfc2253toXMLdsig(String dn) {
37         // Transform from RFC1779 to RFC2253
38         String normalized = normalize(dn, true);
39 
40         return rfctoXML(normalized);
41     }
42 
43     /**
44      * Method xmldsigtoRFC2253
45      *
46      * @param dn
47      * @return normalized string
48      */
xmldsigtoRFC2253(String dn)49     public static String xmldsigtoRFC2253(String dn) {
50         // Transform from RFC1779 to RFC2253
51         String normalized = normalize(dn, false);
52 
53         return xmltoRFC(normalized);
54     }
55 
56     /**
57      * Method normalize
58      *
59      * @param dn
60      * @return normalized string
61      */
normalize(String dn)62     public static String normalize(String dn) {
63         return normalize(dn, true);
64     }
65 
66     /**
67      * Method normalize
68      *
69      * @param dn
70      * @param toXml
71      * @return normalized string
72      */
normalize(String dn, boolean toXml)73     public static String normalize(String dn, boolean toXml) {
74         //if empty string
75         if (dn == null || dn.equals("")) {
76             return "";
77         }
78 
79         try {
80             String DN = semicolonToComma(dn);
81             StringBuilder sb = new StringBuilder();
82             int i = 0;
83             int l = 0;
84             int k;
85 
86             //for name component
87             for (int j = 0; (k = DN.indexOf(',', j)) >= 0; j = k + 1) {
88                 l += countQuotes(DN, j, k);
89 
90                 if (k > 0 && DN.charAt(k - 1) != '\\' && (l % 2) == 0) {
91                     sb.append(parseRDN(DN.substring(i, k).trim(), toXml)).append(",");
92 
93                     i = k + 1;
94                     l = 0;
95                 }
96             }
97 
98             sb.append(parseRDN(trim(DN.substring(i)), toXml));
99 
100             return sb.toString();
101         } catch (IOException ex) {
102             return dn;
103         }
104     }
105 
106     /**
107      * Method parseRDN
108      *
109      * @param str
110      * @param toXml
111      * @return normalized string
112      * @throws IOException
113      */
parseRDN(String str, boolean toXml)114     static String parseRDN(String str, boolean toXml) throws IOException {
115         StringBuilder sb = new StringBuilder();
116         int i = 0;
117         int l = 0;
118         int k;
119 
120         for (int j = 0; (k = str.indexOf('+', j)) >= 0; j = k + 1) {
121             l += countQuotes(str, j, k);
122 
123             if (k > 0 && str.charAt(k - 1) != '\\' && (l % 2) == 0) {
124                 sb.append(parseATAV(trim(str.substring(i, k)), toXml)).append("+");
125 
126                 i = k + 1;
127                 l = 0;
128             }
129         }
130 
131         sb.append(parseATAV(trim(str.substring(i)), toXml));
132 
133         return sb.toString();
134     }
135 
136     /**
137      * Method parseATAV
138      *
139      * @param str
140      * @param toXml
141      * @return normalized string
142      * @throws IOException
143      */
parseATAV(String str, boolean toXml)144     static String parseATAV(String str, boolean toXml) throws IOException {
145         int i = str.indexOf('=');
146 
147         if (i == -1 || i > 0 && str.charAt(i - 1) == '\\') {
148             return str;
149         }
150         String attrType = normalizeAT(str.substring(0, i));
151         // only normalize if value is a String
152         String attrValue = null;
153         if (attrType.charAt(0) >= '0' && attrType.charAt(0) <= '9') {
154             attrValue = str.substring(i + 1);
155         } else {
156             attrValue = normalizeV(str.substring(i + 1), toXml);
157         }
158 
159         return attrType + "=" + attrValue;
160 
161     }
162 
163     /**
164      * Method normalizeAT
165      *
166      * @param str
167      * @return normalized string
168      */
normalizeAT(String str)169     static String normalizeAT(String str) {
170 
171         String at = str.toUpperCase().trim();
172 
173         if (at.startsWith("OID")) {
174             at = at.substring(3);
175         }
176 
177         return at;
178     }
179 
180     /**
181      * Method normalizeV
182      *
183      * @param str
184      * @param toXml
185      * @return normalized string
186      * @throws IOException
187      */
normalizeV(String str, boolean toXml)188     static String normalizeV(String str, boolean toXml) throws IOException {
189         String value = trim(str);
190 
191         if (value.startsWith("\"")) {
192             StringBuilder sb = new StringBuilder();
193             StringReader sr = new StringReader(value.substring(1, value.length() - 1));
194             int i = 0;
195             char c;
196 
197             while ((i = sr.read()) > -1) {
198                 c = (char) i;
199 
200                 //the following char is defined at 4.Relationship with RFC1779 and LDAPv2 inrfc2253
201                 if (c == ',' || c == '=' || c == '+' || c == '<'
202                     || c == '>' || c == '#' || c == ';') {
203                     sb.append('\\');
204                 }
205 
206                 sb.append(c);
207             }
208 
209             value = trim(sb.toString());
210         }
211 
212         if (toXml) {
213             if (value.startsWith("#")) {
214                 value = '\\' + value;
215             }
216         } else {
217             if (value.startsWith("\\#")) {
218                 value = value.substring(1);
219             }
220         }
221 
222         return value;
223     }
224 
225     /**
226      * Method rfctoXML
227      *
228      * @param string
229      * @return normalized string
230      */
rfctoXML(String string)231     static String rfctoXML(String string) {
232         try {
233             String s = changeLess32toXML(string);
234 
235             return changeWStoXML(s);
236         } catch (Exception e) {
237             return string;
238         }
239     }
240 
241     /**
242      * Method xmltoRFC
243      *
244      * @param string
245      * @return normalized string
246      */
xmltoRFC(String string)247     static String xmltoRFC(String string) {
248         try {
249             String s = changeLess32toRFC(string);
250 
251             return changeWStoRFC(s);
252         } catch (Exception e) {
253             return string;
254         }
255     }
256 
257     /**
258      * Method changeLess32toRFC
259      *
260      * @param string
261      * @return normalized string
262      * @throws IOException
263      */
changeLess32toRFC(String string)264     static String changeLess32toRFC(String string) throws IOException {
265         StringBuilder sb = new StringBuilder();
266         StringReader sr = new StringReader(string);
267         int i = 0;
268         char c;
269 
270         while ((i = sr.read()) > -1) {
271             c = (char) i;
272 
273             if (c == '\\') {
274                 sb.append(c);
275 
276                 char c1 = (char) sr.read();
277                 char c2 = (char) sr.read();
278 
279                 //65 (A) 97 (a)
280                 if ((c1 >= 48 && c1 <= 57 || c1 >= 65 && c1 <= 70 || c1 >= 97 && c1 <= 102)
281                     && (c2 >= 48 && c2 <= 57
282                         || c2 >= 65 && c2 <= 70
283                         || c2 >= 97 && c2 <= 102)) {
284                     char ch = (char) Byte.parseByte("" + c1 + c2, 16);
285 
286                     sb.append(ch);
287                 } else {
288                     sb.append(c1);
289                     sb.append(c2);
290                 }
291             } else {
292                 sb.append(c);
293             }
294         }
295 
296         return sb.toString();
297     }
298 
299     /**
300      * Method changeLess32toXML
301      *
302      * @param string
303      * @return normalized string
304      * @throws IOException
305      */
changeLess32toXML(String string)306     static String changeLess32toXML(String string) throws IOException {
307         StringBuilder sb = new StringBuilder();
308         StringReader sr = new StringReader(string);
309         int i = 0;
310 
311         while ((i = sr.read()) > -1) {
312             if (i < 32) {
313                 sb.append('\\');
314                 sb.append(Integer.toHexString(i));
315             } else {
316                 sb.append((char) i);
317             }
318         }
319 
320         return sb.toString();
321     }
322 
323     /**
324      * Method changeWStoXML
325      *
326      * @param string
327      * @return normalized string
328      * @throws IOException
329      */
changeWStoXML(String string)330     static String changeWStoXML(String string) throws IOException {
331         StringBuilder sb = new StringBuilder();
332         StringReader sr = new StringReader(string);
333         int i = 0;
334         char c;
335 
336         while ((i = sr.read()) > -1) {
337             c = (char) i;
338 
339             if (c == '\\') {
340                 char c1 = (char) sr.read();
341 
342                 if (c1 == ' ') {
343                     sb.append('\\');
344 
345                     String s = "20";
346 
347                     sb.append(s);
348                 } else {
349                     sb.append('\\');
350                     sb.append(c1);
351                 }
352             } else {
353                 sb.append(c);
354             }
355         }
356 
357         return sb.toString();
358     }
359 
360     /**
361      * Method changeWStoRFC
362      *
363      * @param string
364      * @return normalized string
365      */
changeWStoRFC(String string)366     static String changeWStoRFC(String string) {
367         StringBuilder sb = new StringBuilder();
368         int i = 0;
369         int k;
370 
371         for (int j = 0; (k = string.indexOf("\\20", j)) >= 0; j = k + 3) {
372             sb.append(trim(string.substring(i, k))).append("\\ ");
373 
374             i = k + 3;
375         }
376 
377         sb.append(string.substring(i));
378 
379         return sb.toString();
380     }
381 
382     /**
383      * Method semicolonToComma
384      *
385      * @param str
386      * @return normalized string
387      */
semicolonToComma(String str)388     static String semicolonToComma(String str) {
389         return removeWSandReplace(str, ";", ",");
390     }
391 
392     /**
393      * Method removeWhiteSpace
394      *
395      * @param str
396      * @param symbol
397      * @return normalized string
398      */
removeWhiteSpace(String str, String symbol)399     static String removeWhiteSpace(String str, String symbol) {
400         return removeWSandReplace(str, symbol, symbol);
401     }
402 
403     /**
404      * Method removeWSandReplace
405      *
406      * @param str
407      * @param symbol
408      * @param replace
409      * @return normalized string
410      */
removeWSandReplace(String str, String symbol, String replace)411     static String removeWSandReplace(String str, String symbol, String replace) {
412         StringBuilder sb = new StringBuilder();
413         int i = 0;
414         int l = 0;
415         int k;
416 
417         for (int j = 0; (k = str.indexOf(symbol, j)) >= 0; j = k + 1) {
418             l += countQuotes(str, j, k);
419 
420             if (k > 0 && str.charAt(k - 1) != '\\' && (l % 2) == 0) {
421                 sb.append(trim(str.substring(i, k))).append(replace);
422 
423                 i = k + 1;
424                 l = 0;
425             }
426         }
427 
428         sb.append(trim(str.substring(i)));
429 
430         return sb.toString();
431     }
432 
433     /**
434      * Returns the number of Quotation from i to j
435      *
436      * @param s
437      * @param i
438      * @param j
439      * @return number of quotes
440      */
countQuotes(String s, int i, int j)441     private static int countQuotes(String s, int i, int j) {
442         int k = 0;
443 
444         for (int l = i; l < j; l++) {
445             if (s.charAt(l) == '"') {
446                 k++;
447             }
448         }
449 
450         return k;
451     }
452 
453     //only for the end of a space character occurring at the end of the string from rfc2253
454 
455     /**
456      * Method trim
457      *
458      * @param str
459      * @return the string
460      */
trim(String str)461     static String trim(String str) {
462 
463         String trimed = str.trim();
464         int i = str.indexOf(trimed) + trimed.length();
465 
466         if (str.length() > i && trimed.endsWith("\\")
467             && !trimed.endsWith("\\\\") && str.charAt(i) == ' ') {
468             trimed = trimed + " ";
469         }
470 
471         return trimed;
472     }
473 
474 }
475