1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright  1999-2004 The Apache Software Foundation.
   7  *
   8  *  Licensed under the Apache License, Version 2.0 (the "License");
   9  *  you may not use this file except in compliance with the License.
  10  *  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  *  Unless required by applicable law or agreed to in writing, software
  15  *  distributed under the License is distributed on an "AS IS" BASIS,
  16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  *  See the License for the specific language governing permissions and
  18  *  limitations under the License.
  19  *
  20  */
  21 package com.sun.org.apache.xml.internal.security.utils;
  22 
  23 
  24 
  25 import java.io.IOException;
  26 import java.io.StringReader;
  27 
  28 
  29 /**
  30  *
  31  * @author $Author: mullan $
  32  */
  33 public class RFC2253Parser {
  34 
  35 
  36    /** {@link java.util.logging} logging facility */
  37    /* static java.util.logging.Logger log =
  38         java.util.logging.Logger.getLogger(RFC2253Parser.class.getName());
  39    */
  40 
  41    static boolean _TOXML = true;
  42 
  43    /**
  44     * Method rfc2253toXMLdsig
  45     *
  46     * @param dn
  47     * @return normalized string
  48     *
  49     */
  50    public static String rfc2253toXMLdsig(String dn) {
  51 
  52       _TOXML = true;
  53 
  54       // Transform from RFC1779 to RFC2253
  55       String normalized = normalize(dn);
  56 
  57       return rfctoXML(normalized);
  58    }
  59 
  60    /**
  61     * Method xmldsigtoRFC2253
  62     *
  63     * @param dn
  64     * @return normalized string
  65     */
  66    public static String xmldsigtoRFC2253(String dn) {
  67 
  68       _TOXML = false;
  69 
  70       // Transform from RFC1779 to RFC2253
  71       String normalized = normalize(dn);
  72 
  73       return xmltoRFC(normalized);
  74    }
  75 
  76    /**
  77     * Method normalize
  78     *
  79     * @param dn
  80     * @return normalized string
  81     */
  82    public static String normalize(String dn) {
  83 
  84       //if empty string
  85       if ((dn == null) || dn.equals("")) {
  86          return "";
  87       }
  88 
  89       try {
  90          String _DN = semicolonToComma(dn);
  91          StringBuffer sb = new StringBuffer();
  92          int i = 0;
  93          int l = 0;
  94          int k;
  95 
  96          //for name component
  97          for (int j = 0; (k = _DN.indexOf(",", j)) >= 0; j = k + 1) {
  98             l += countQuotes(_DN, j, k);
  99 
 100             if ((k > 0) && (_DN.charAt(k - 1) != '\\') && (l % 2) != 1) {
 101                sb.append(parseRDN(_DN.substring(i, k).trim()) + ",");
 102 
 103                i = k + 1;
 104                l = 0;
 105             }
 106          }
 107 
 108          sb.append(parseRDN(trim(_DN.substring(i))));
 109 
 110          return sb.toString();
 111       } catch (IOException ex) {
 112          return dn;
 113       }
 114    }
 115 
 116    /**
 117     * Method parseRDN
 118     *
 119     * @param str
 120     * @return normalized string
 121     * @throws IOException
 122     */
 123    static String parseRDN(String str) throws IOException {
 124 
 125       StringBuffer sb = new StringBuffer();
 126       int i = 0;
 127       int l = 0;
 128       int k;
 129 
 130       for (int j = 0; (k = str.indexOf("+", j)) >= 0; j = k + 1) {
 131          l += countQuotes(str, j, k);
 132 
 133          if ((k > 0) && (str.charAt(k - 1) != '\\') && (l % 2) != 1) {
 134             sb.append(parseATAV(trim(str.substring(i, k))) + "+");
 135 
 136             i = k + 1;
 137             l = 0;
 138          }
 139       }
 140 
 141       sb.append(parseATAV(trim(str.substring(i))));
 142 
 143       return sb.toString();
 144    }
 145 
 146    /**
 147     * Method parseATAV
 148     *
 149     * @param str
 150     * @return normalized string
 151     * @throws IOException
 152     */
 153    static String parseATAV(String str) throws IOException {
 154 
 155       int i = str.indexOf("=");
 156 
 157       if ((i == -1) || ((i > 0) && (str.charAt(i - 1) == '\\'))) {
 158          return str;
 159       }
 160       String attrType = normalizeAT(str.substring(0, i));
 161       // only normalize if value is a String
 162       String attrValue = null;
 163       if (attrType.charAt(0) >= '0' && attrType.charAt(0) <= '9') {
 164           attrValue = str.substring(i + 1);
 165       } else {
 166           attrValue = normalizeV(str.substring(i + 1));
 167       }
 168 
 169       return attrType + "=" + attrValue;
 170 
 171    }
 172 
 173    /**
 174     * Method normalizeAT
 175     *
 176     * @param str
 177     * @return normalized string
 178     */
 179    static String normalizeAT(String str) {
 180 
 181       String at = str.toUpperCase().trim();
 182 
 183       if (at.startsWith("OID")) {
 184          at = at.substring(3);
 185       }
 186 
 187       return at;
 188    }
 189 
 190    /**
 191     * Method normalizeV
 192     *
 193     * @param str
 194     * @return normalized string
 195     * @throws IOException
 196     */
 197    static String normalizeV(String str) throws IOException {
 198 
 199       String value = trim(str);
 200 
 201       if (value.startsWith("\"")) {
 202          StringBuffer sb = new StringBuffer();
 203          StringReader sr = new StringReader(value.substring(1,
 204                               value.length() - 1));
 205          int i = 0;
 206          char c;
 207 
 208          for (; (i = sr.read()) > -1; ) {
 209             c = (char) i;
 210 
 211             //the following char is defined at 4.Relationship with RFC1779 and LDAPv2 inrfc2253
 212             if ((c == ',') || (c == '=') || (c == '+') || (c == '<')
 213                     || (c == '>') || (c == '#') || (c == ';')) {
 214                sb.append('\\');
 215             }
 216 
 217             sb.append(c);
 218          }
 219 
 220          value = trim(sb.toString());
 221       }
 222 
 223       if (_TOXML == true) {
 224          if (value.startsWith("#")) {
 225             value = '\\' + value;
 226          }
 227       } else {
 228          if (value.startsWith("\\#")) {
 229             value = value.substring(1);
 230          }
 231       }
 232 
 233       return value;
 234    }
 235 
 236    /**
 237     * Method rfctoXML
 238     *
 239     * @param string
 240     * @return normalized string
 241     */
 242    static String rfctoXML(String string) {
 243 
 244       try {
 245          String s = changeLess32toXML(string);
 246 
 247          return changeWStoXML(s);
 248       } catch (Exception e) {
 249          return string;
 250       }
 251    }
 252 
 253    /**
 254     * Method xmltoRFC
 255     *
 256     * @param string
 257     * @return normalized string
 258     */
 259    static String xmltoRFC(String string) {
 260 
 261       try {
 262          String s = changeLess32toRFC(string);
 263 
 264          return changeWStoRFC(s);
 265       } catch (Exception e) {
 266          return string;
 267       }
 268    }
 269 
 270    /**
 271     * Method changeLess32toRFC
 272     *
 273     * @param string
 274     * @return normalized string
 275     * @throws IOException
 276     */
 277    static String changeLess32toRFC(String string) throws IOException {
 278 
 279       StringBuffer sb = new StringBuffer();
 280       StringReader sr = new StringReader(string);
 281       int i = 0;
 282       char c;
 283 
 284       for (; (i = sr.read()) > -1; ) {
 285          c = (char) i;
 286 
 287          if (c == '\\') {
 288             sb.append(c);
 289 
 290             char c1 = (char) sr.read();
 291             char c2 = (char) sr.read();
 292 
 293             //65 (A) 97 (a)
 294             if ((((c1 >= 48) && (c1 <= 57)) || ((c1 >= 65) && (c1 <= 70)) || ((c1 >= 97) && (c1 <= 102)))
 295                     && (((c2 >= 48) && (c2 <= 57))
 296                         || ((c2 >= 65) && (c2 <= 70))
 297                         || ((c2 >= 97) && (c2 <= 102)))) {
 298                char ch = (char) Byte.parseByte("" + c1 + c2, 16);
 299 
 300                sb.append(ch);
 301             } else {
 302                sb.append(c1);
 303                sb.append(c2);
 304             }
 305          } else {
 306             sb.append(c);
 307          }
 308       }
 309 
 310       return sb.toString();
 311    }
 312 
 313    /**
 314     * Method changeLess32toXML
 315     *
 316     * @param string
 317     * @return normalized string
 318     * @throws IOException
 319     */
 320    static String changeLess32toXML(String string) throws IOException {
 321 
 322       StringBuffer sb = new StringBuffer();
 323       StringReader sr = new StringReader(string);
 324       int i = 0;
 325 
 326       for (; (i = sr.read()) > -1; ) {
 327          if (i < 32) {
 328             sb.append('\\');
 329             sb.append(Integer.toHexString(i));
 330          } else {
 331             sb.append((char) i);
 332          }
 333       }
 334 
 335       return sb.toString();
 336    }
 337 
 338    /**
 339     * Method changeWStoXML
 340     *
 341     * @param string
 342     * @return normalized string
 343     * @throws IOException
 344     */
 345    static String changeWStoXML(String string) throws IOException {
 346 
 347       StringBuffer sb = new StringBuffer();
 348       StringReader sr = new StringReader(string);
 349       int i = 0;
 350       char c;
 351 
 352       for (; (i = sr.read()) > -1; ) {
 353          c = (char) i;
 354 
 355          if (c == '\\') {
 356             char c1 = (char) sr.read();
 357 
 358             if (c1 == ' ') {
 359                sb.append('\\');
 360 
 361                String s = "20";
 362 
 363                sb.append(s);
 364             } else {
 365                sb.append('\\');
 366                sb.append(c1);
 367             }
 368          } else {
 369             sb.append(c);
 370          }
 371       }
 372 
 373       return sb.toString();
 374    }
 375 
 376    /**
 377     * Method changeWStoRFC
 378     *
 379     * @param string
 380     * @return normalized string
 381     */
 382    static String changeWStoRFC(String string) {
 383 
 384       StringBuffer sb = new StringBuffer();
 385       int i = 0;
 386       int k;
 387 
 388       for (int j = 0; (k = string.indexOf("\\20", j)) >= 0; j = k + 3) {
 389          sb.append(trim(string.substring(i, k)) + "\\ ");
 390 
 391          i = k + 3;
 392       }
 393 
 394       sb.append(string.substring(i));
 395 
 396       return sb.toString();
 397    }
 398 
 399    /**
 400     * Method semicolonToComma
 401     *
 402     * @param str
 403     * @return normalized string
 404     */
 405    static String semicolonToComma(String str) {
 406       return removeWSandReplace(str, ";", ",");
 407    }
 408 
 409    /**
 410     * Method removeWhiteSpace
 411     *
 412     * @param str
 413     * @param symbol
 414     * @return normalized string
 415     */
 416    static String removeWhiteSpace(String str, String symbol) {
 417       return removeWSandReplace(str, symbol, symbol);
 418    }
 419 
 420    /**
 421     * Method removeWSandReplace
 422     *
 423     * @param str
 424     * @param symbol
 425     * @param replace
 426     * @return normalized string
 427     */
 428    static String removeWSandReplace(String str, String symbol, String replace) {
 429 
 430       StringBuffer sb = new StringBuffer();
 431       int i = 0;
 432       int l = 0;
 433       int k;
 434 
 435       for (int j = 0; (k = str.indexOf(symbol, j)) >= 0; j = k + 1) {
 436          l += countQuotes(str, j, k);
 437 
 438          if ((k > 0) && (str.charAt(k - 1) != '\\') && (l % 2) != 1) {
 439             sb.append(trim(str.substring(i, k)) + replace);
 440 
 441             i = k + 1;
 442             l = 0;
 443          }
 444       }
 445 
 446       sb.append(trim(str.substring(i)));
 447 
 448       return sb.toString();
 449    }
 450 
 451    /**
 452     * Returns the number of Quotation from i to j
 453     *
 454     * @param s
 455     * @param i
 456     * @param j
 457     * @return number of quotes
 458     */
 459    private static int countQuotes(String s, int i, int j) {
 460 
 461       int k = 0;
 462 
 463       for (int l = i; l < j; l++) {
 464          if (s.charAt(l) == '"') {
 465             k++;
 466          }
 467       }
 468 
 469       return k;
 470    }
 471 
 472    //only for the end of a space character occurring at the end of the string from rfc2253
 473 
 474    /**
 475     * Method trim
 476     *
 477     * @param str
 478     * @return the string
 479     */
 480    static String trim(String str) {
 481 
 482       String trimed = str.trim();
 483       int i = str.indexOf(trimed) + trimed.length();
 484 
 485       if ((str.length() > i) && trimed.endsWith("\\")
 486               &&!trimed.endsWith("\\\\")) {
 487          if (str.charAt(i) == ' ') {
 488             trimed = trimed + " ";
 489          }
 490       }
 491 
 492       return trimed;
 493    }
 494 
 495    /**
 496     * Method main
 497     *
 498     * @param args
 499     * @throws Exception
 500     */
 501    public static void main(String[] args) throws Exception {
 502 
 503       testToXML("CN=\"Steve, Kille\",  O=Isode Limited, C=GB");
 504       testToXML("CN=Steve Kille    ,   O=Isode Limited,C=GB");
 505       testToXML("\\ OU=Sales+CN=J. Smith,O=Widget Inc.,C=US\\ \\ ");
 506       testToXML("CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB");
 507       testToXML("CN=Before\\0DAfter,O=Test,C=GB");
 508       testToXML("CN=\"L. Eagle,O=Sue, = + < > # ;Grabbit and Runn\",C=GB");
 509       testToXML("1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB");
 510 
 511       {
 512          StringBuffer sb = new StringBuffer();
 513 
 514          sb.append('L');
 515          sb.append('u');
 516          sb.append('\uc48d');
 517          sb.append('i');
 518          sb.append('\uc487');
 519 
 520          String test7 = "SN=" + sb.toString();
 521 
 522          testToXML(test7);
 523       }
 524 
 525       testToRFC("CN=\"Steve, Kille\",  O=Isode Limited, C=GB");
 526       testToRFC("CN=Steve Kille    ,   O=Isode Limited,C=GB");
 527       testToRFC("\\20OU=Sales+CN=J. Smith,O=Widget Inc.,C=US\\20\\20 ");
 528       testToRFC("CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB");
 529       testToRFC("CN=Before\\12After,O=Test,C=GB");
 530       testToRFC("CN=\"L. Eagle,O=Sue, = + < > # ;Grabbit and Runn\",C=GB");
 531       testToRFC("1.3.6.1.4.1.1466.0=\\#04024869,O=Test,C=GB");
 532 
 533       {
 534          StringBuffer sb = new StringBuffer();
 535 
 536          sb.append('L');
 537          sb.append('u');
 538          sb.append('\uc48d');
 539          sb.append('i');
 540          sb.append('\uc487');
 541 
 542          String test7 = "SN=" + sb.toString();
 543 
 544          testToRFC(test7);
 545       }
 546    }
 547 
 548    /** Field i */
 549    static int counter = 0;
 550 
 551    /**
 552     * Method test
 553     *
 554     * @param st
 555     */
 556    static void testToXML(String st) {
 557 
 558       System.out.println("start " + counter++ + ": " + st);
 559       System.out.println("         " + rfc2253toXMLdsig(st));
 560       System.out.println("");
 561    }
 562 
 563    /**
 564     * Method testToRFC
 565     *
 566     * @param st
 567     */
 568    static void testToRFC(String st) {
 569 
 570       System.out.println("start " + counter++ + ": " + st);
 571       System.out.println("         " + xmldsigtoRFC2253(st));
 572       System.out.println("");
 573    }
 574 }