001: /*************************************************************************
002: * *
003: * EJBCA: The OpenSource Certificate Authority *
004: * *
005: * This software is free software; you can redistribute it and/or *
006: * modify it under the terms of the GNU Lesser General Public *
007: * License as published by the Free Software Foundation; either *
008: * version 2.1 of the License, or any later version. *
009: * *
010: * See terms of license at gnu.org. *
011: * *
012: *************************************************************************/package org.ejbca.util.dn;
013:
014: import java.io.BufferedReader;
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.io.InputStreamReader;
018: import java.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.LinkedHashMap;
021: import java.util.Set;
022:
023: import org.apache.commons.lang.ArrayUtils;
024: import org.apache.commons.lang.BooleanUtils;
025: import org.apache.commons.lang.StringUtils;
026: import org.apache.log4j.Logger;
027: import org.bouncycastle.asn1.DERObjectIdentifier;
028: import org.bouncycastle.asn1.x509.X509Name;
029:
030: /** Class holding information and utilitites for handling different DN components, CN, O etc
031: *
032: * This is a very complex class with lots of maps and stuff. It is because it is a first step of refactoring the DN/AltName/DirAttr handling.
033: * This previously consisted of lotsa of different arrays spread out all over the place, now it's gathered here in order to be able to get a view of it.
034: * The underlying implementations have not changed much though, in order to still have things working, therefore there are lots of different maps and arrays, with
035: * seemingly similar contents.
036: *
037: * @author tomas
038: * @version $Id: DnComponents.java,v 1.11 2008/02/26 17:04:05 jeklund Exp $
039: */
040: public class DnComponents {
041: private static Logger log = Logger.getLogger(DnComponents.class);
042:
043: /** This class should be instantiated immediately */
044: private static DnComponents obj = new DnComponents();
045:
046: /** BC X509Name contains some lookup tables that could maybe be used here.
047: *
048: * This map is used in CertTools so sort and order DN strings so they all look the same in the database.
049: * */
050: private static HashMap oids = new HashMap();
051: // Default values
052: static {
053: oids.put("c", X509Name.C);
054: oids.put("dc", X509Name.DC);
055: oids.put("st", X509Name.ST);
056: oids.put("l", X509Name.L);
057: oids.put("o", X509Name.O);
058: oids.put("ou", X509Name.OU);
059: oids.put("t", X509Name.T);
060: oids.put("surname", X509Name.SURNAME);
061: oids.put("initials", X509Name.INITIALS);
062: oids.put("givenname", X509Name.GIVENNAME);
063: oids.put("gn", X509Name.GIVENNAME);
064: oids.put("sn", X509Name.SN);
065: oids.put("serialnumber", X509Name.SN);
066: oids.put("cn", X509Name.CN);
067: oids.put("uid", X509Name.UID);
068: oids.put("dn", X509Name.DN_QUALIFIER);
069: oids.put("emailaddress", X509Name.EmailAddress);
070: oids.put("e", X509Name.EmailAddress);
071: oids.put("email", X509Name.EmailAddress);
072: oids.put("unstructuredname", X509Name.UnstructuredName); //unstructuredName
073: oids.put("unstructuredaddress", X509Name.UnstructuredAddress); //unstructuredAddress
074: oids.put("postalcode", X509Name.POSTAL_CODE);
075: oids.put("businesscategory", X509Name.BUSINESS_CATEGORY);
076: }
077: /** Default values used when constructing DN strings that are put in the database
078: *
079: */
080: private static String[] dNObjectsForward = { "businesscategory",
081: "postalcode", "unstructuredaddress", "unstructuredname",
082: "emailaddress", "e", "email", "dn", "uid", "cn", "sn",
083: "serialnumber", "gn", "givenname", "initials", "surname",
084: "t", "ou", "o", "l", "st", "dc", "c" };
085: // Default values
086: private static String[] dNObjectsReverse = null;
087:
088: /**
089: * These maps and constants are used in the admin-GUI and in End Entity profiles
090: */
091:
092: /** These constants can be used when reffering to standard, build in components
093: *
094: */
095: // DN components
096: public static final String DNEMAIL = "DNEMAIL";
097: public static final String DNQUALIFIER = "DN";
098: public static final String UID = "UID";
099: public static final String COMMONNAME = "COMMONNAME";
100: public static final String SN = "SN";
101: public static final String GIVENNAME = "GIVENNAME";
102: public static final String INITIALS = "INITIALS";
103: public static final String SURNAME = "SURNAME";
104: public static final String TITLE = "TITLE";
105: public static final String ORGANIZATIONUNIT = "ORGANIZATIONUNIT";
106: public static final String ORGANIZATION = "ORGANIZATION";
107: public static final String LOCALE = "LOCALE";
108: public static final String STATE = "STATE";
109: public static final String DOMAINCOMPONENT = "DOMAINCOMPONENT";
110: public static final String COUNTRY = "COUNTRY";
111: public static final String UNSTRUCTUREDADDRESS = "UNSTRUCTUREDADDRESS";
112: public static final String UNSTRUCTUREDNAME = "UNSTRUCTUREDNAME";
113: public static final String POSTALCODE = "POSTALCODE";
114: public static final String BUSINESSCATEGORY = "BUSINESSCATEGORY";
115:
116: // AltNames
117: public static final String RFC822NAME = "RFC822NAME";
118: public static final String DNSNAME = "DNSNAME";
119: public static final String IPADDRESS = "IPADDRESS";
120: public static final String UNIFORMRESOURCEID = "UNIFORMRESOURCEID";
121: public static final String DIRECTORYNAME = "DIRECTORYNAME";
122: public static final String UPN = "UPN";
123: public static final String GUID = "GUID";
124: // Below are altNames that are not implemented yet
125: public static final String OTHERNAME = "OTHERNAME";
126: public static final String X400ADDRESS = "X400ADDRESS";
127: public static final String EDIPARTNAME = "EDIPARTNAME";
128: public static final String REGISTEREDID = "REGISTEREDID";
129:
130: // Subject directory attributes
131: public static final String DATEOFBIRTH = "DATEOFBIRTH";
132: public static final String PLACEOFBIRTH = "PLACEOFBIRTH";
133: public static final String GENDER = "GENDER";
134: public static final String COUNTRYOFCITIZENSHIP = "COUNTRYOFCITIZENSHIP";
135: public static final String COUNTRYOFRESIDENCE = "COUNTRYOFRESIDENCE";
136:
137: private static HashMap dnNameIdMap = new HashMap();
138: private static HashMap profileNameIdMap = new HashMap();
139: private static HashMap dnIdToProfileNameMap = new HashMap();
140: private static HashMap dnIdToProfileIdMap = new HashMap();
141: private static HashMap profileIdToDnIdMap = new HashMap();
142: private static HashMap dnErrorTextMap = new HashMap();
143: private static HashMap profileNameLanguageMap = new HashMap();
144: private static HashMap profileIdLanguageMap = new HashMap();
145: private static HashMap dnIdErrorMap = new HashMap();
146: private static HashMap dnIdToExtractorFieldMap = new HashMap();
147: private static HashMap altNameIdToExtractorFieldMap = new HashMap();
148: private static HashMap dirAttrIdToExtractorFieldMap = new HashMap();
149: private static ArrayList dnProfileFields = new ArrayList();
150: private static ArrayList dnLanguageTexts = new ArrayList();
151: private static ArrayList dnDnIds = new ArrayList();
152: private static ArrayList altNameFields = new ArrayList();
153: private static ArrayList altNameLanguageTexts = new ArrayList();
154: private static ArrayList altNameDnIds = new ArrayList();
155: private static ArrayList dirAttrFields = new ArrayList();
156: private static ArrayList dirAttrLanguageTexts = new ArrayList();
157: private static ArrayList dirAttrDnIds = new ArrayList();
158: private static ArrayList dnExtractorFields = new ArrayList();
159: private static ArrayList altNameExtractorFields = new ArrayList();
160: private static ArrayList dirAttrExtractorFields = new ArrayList();
161:
162: // Load values from a properties file, if it exists
163: static {
164: DnComponents.load();
165: }
166: /** This property is true if reverse DN order should be used. Default value is false (forward order).
167: * This setting is changed from ejbca.properties
168: */
169: private static final boolean reverseOrder = BooleanUtils
170: .toBoolean("@certtools.dnorderreverse@");
171:
172: public static boolean isReverseOrder() {
173: return reverseOrder == true;
174: }
175:
176: public static DERObjectIdentifier getOid(String o) {
177: return (DERObjectIdentifier) oids.get(o.toLowerCase());
178: } // getOid
179:
180: public static ArrayList getDnProfileFields() {
181: return dnProfileFields;
182: }
183:
184: public static ArrayList getDnLanguageTexts() {
185: return dnLanguageTexts;
186: }
187:
188: public static ArrayList getAltNameFields() {
189: return altNameFields;
190: }
191:
192: public static ArrayList getAltNameLanguageTexts() {
193: return altNameLanguageTexts;
194: }
195:
196: public static ArrayList getDirAttrFields() {
197: return dirAttrFields;
198: }
199:
200: // Used by DNFieldExtractor and EntityProfile, don't USE
201: public static ArrayList getDirAttrDnIds() {
202: return dirAttrDnIds;
203: }
204:
205: // Used by DNFieldExtractor and EntityProfile, don't USE
206: public static ArrayList getAltNameDnIds() {
207: return altNameDnIds;
208: }
209:
210: // Used by DNFieldExtractor and EntityProfile, don't USE
211: public static ArrayList getDnDnIds() {
212: return dnDnIds;
213: }
214:
215: // Used only by DNFieldExtractor, don't USE
216: protected static ArrayList getDnExtractorFields() {
217: return dnExtractorFields;
218: }
219:
220: protected static String getDnExtractorFieldFromDnId(int field) {
221: String val = (String) dnIdToExtractorFieldMap.get(new Integer(
222: field));
223: return val;
224: }
225:
226: // Used only by DNFieldExtractor, don't USE
227: protected static ArrayList getAltNameExtractorFields() {
228: return altNameExtractorFields;
229: }
230:
231: protected static String getAltNameExtractorFieldFromDnId(int field) {
232: String val = (String) altNameIdToExtractorFieldMap
233: .get(new Integer(field));
234: return val;
235: }
236:
237: // Used only by DNFieldExtractor, don't USE
238: protected static ArrayList getDirAttrExtractorFields() {
239: return dirAttrExtractorFields;
240: }
241:
242: protected static String getDirAttrExtractorFieldFromDnId(int field) {
243: String val = (String) dirAttrIdToExtractorFieldMap
244: .get(new Integer(field));
245: return val;
246: }
247:
248: public static String dnIdToProfileName(int dnid) {
249: String val = (String) dnIdToProfileNameMap
250: .get(new Integer(dnid));
251: return val;
252: }
253:
254: public static int dnIdToProfileId(int dnid) {
255: Integer val = (Integer) dnIdToProfileIdMap
256: .get(new Integer(dnid));
257: return val.intValue();
258: }
259:
260: /**
261: * Method to get a language error constant for the admin-GUI from a profile name
262: */
263: public static String getLanguageConstantFromProfileName(String name) {
264: String ret = (String) profileNameLanguageMap.get(name);
265: return ret;
266: }
267:
268: /**
269: * Method to get a language error constant for the admin-GUI from a profile id
270: */
271: public static String getLanguageConstantFromProfileId(int id) {
272: String ret = (String) profileIdLanguageMap.get(new Integer(id));
273: return ret;
274: }
275:
276: /**
277: * Method to get a clear text error msg for the admin-GUI from a dn id
278: */
279: public static String getErrTextFromDnId(int id) {
280: String ret = (String) dnIdErrorMap.get(new Integer(id));
281: return ret;
282: }
283:
284: /** This method is only used to initialize EndEntityProfile, because of legacy baggage.
285: * Should be refactored sometime! Please don't use this whatever you do!
286: * @return
287: */
288: public static HashMap getProfilenameIdMap() {
289: return profileNameIdMap;
290:
291: }
292:
293: /** A function that takes an fieldid pointing to a coresponding id in UserView and DnFieldExctractor.
294: * For example : profileFieldIdToUserFieldIdMapper(EndEntityProfile.COMMONNAME) returns DnFieldExctractor.COMMONNAME.
295: *
296: * Should only be used with subjectDN, Subject Alternative Names and subject directory attribute fields.
297: */
298: public static int profileIdToDnId(int profileid) {
299: Integer val = (Integer) profileIdToDnIdMap.get(new Integer(
300: profileid));
301: if (val == null) {
302: log.error("No dn id mapping from profile id " + profileid);
303: // We allow it to fail here
304: }
305: return val.intValue();
306: }
307:
308: /**
309: * Returns the dnObjects (forward or reverse) that is in use
310: */
311: public static String[] getDnObjects() {
312: if (!reverseOrder) {
313: return dNObjectsForward;
314: }
315: return getDnObjectsReverse();
316: }
317:
318: /**
319: * Returns the dnObjects (forward or reverse).
320: * ldaproder = true is the default order in EJBCA.
321: */
322: public static String[] getDnObjects(boolean ldaporder) {
323: if (ldaporder) {
324: return dNObjectsForward;
325: }
326: return getDnObjectsReverse();
327: }
328:
329: /**
330: * Returns the reversed dnObjects.
331: * Protected to allow testing
332: */
333: protected static String[] getDnObjectsReverse() {
334: // Create and reverse the order if it has not been initialized already
335: if (dNObjectsReverse == null) {
336: // this cast is not needed in java 5, but is needed for java 1.4
337: dNObjectsReverse = (String[]) dNObjectsForward.clone();
338: ArrayUtils.reverse(dNObjectsReverse);
339: }
340: return dNObjectsReverse;
341: }
342:
343: private static void load() {
344: loadOrdering();
345: loadMappings();
346: }
347:
348: /**
349: * Load DN ordering used in CertTools.stringToBCDNString etc.
350: * Loads from file placed in src/dncomponents.properties
351: *
352: * A line is:
353: * DNName;DNid;ProfileName;ProfileId,ErrorString,LanguageConstant
354: *
355: */
356: private static void loadMappings() {
357: // Read the file to an array of lines
358: String line;
359:
360: BufferedReader in = null;
361: InputStreamReader inf = null;
362: try {
363: InputStream is = obj.getClass().getResourceAsStream(
364: "/profilemappings.properties");
365: //log.info("is is: " + is);
366: if (is != null) {
367: inf = new InputStreamReader(is);
368: //inf = new FileReader("c:\\foo.properties");
369: in = new BufferedReader(inf);
370: if (!in.ready())
371: throw new IOException();
372: String[] splits = null;
373: int lines = 0;
374: while ((line = in.readLine()) != null) {
375: if (!line.startsWith("#")) { // # is a comment line
376: splits = StringUtils.split(line, ';');
377: if ((splits != null) && (splits.length > 5)) {
378: String type = splits[0];
379: String dnname = splits[1];
380: Integer dnid = new Integer(splits[2]);
381: String profilename = splits[3];
382: Integer profileid = new Integer(splits[4]);
383: String errstr = splits[5];
384: String langstr = splits[6];
385: // Fill maps
386: dnNameIdMap.put(dnname, dnid);
387: profileNameIdMap
388: .put(profilename, profileid);
389: dnIdToProfileNameMap.put(dnid, profilename);
390: dnIdToProfileIdMap.put(dnid, profileid);
391: dnIdErrorMap.put(dnid, errstr);
392: profileIdToDnIdMap.put(profileid, dnid);
393: dnErrorTextMap.put(dnid, errstr);
394: profileNameLanguageMap.put(profilename,
395: langstr);
396: profileIdLanguageMap
397: .put(profileid, langstr);
398: if (type.equals("DN")) {
399: dnProfileFields.add(profilename);
400: dnLanguageTexts.add(langstr);
401: dnDnIds.add(dnid);
402: dnExtractorFields.add(dnname + "=");
403: dnIdToExtractorFieldMap.put(dnid,
404: dnname + "=");
405: }
406: if (type.equals("ALTNAME")) {
407: altNameFields.add(dnname);
408: altNameLanguageTexts.add(langstr);
409: altNameDnIds.add(dnid);
410: altNameExtractorFields
411: .add(dnname + "=");
412: altNameIdToExtractorFieldMap.put(dnid,
413: dnname + "=");
414: }
415: if (type.equals("DIRATTR")) {
416: dirAttrFields.add(dnname);
417: dirAttrLanguageTexts.add(langstr);
418: dirAttrDnIds.add(dnid);
419: dirAttrExtractorFields
420: .add(dnname + "=");
421: dirAttrIdToExtractorFieldMap.put(dnid,
422: dnname + "=");
423: }
424: lines++;
425: }
426: }
427: }
428: in.close();
429: log
430: .debug("Read profile maps with " + lines
431: + " lines.");
432: } else {
433: throw new IOException(
434: "Input stream for /profilemappings.properties is null");
435: }
436: } catch (IOException e) {
437: log.error("Can not load profile mappings: ", e);
438: } finally {
439: try {
440: if (inf != null)
441: inf.close();
442: if (in != null)
443: in.close();
444: } catch (IOException e) {
445: }
446: }
447:
448: }
449:
450: /**
451: * Load DN ordering used in CertTools.stringToBCDNString etc.
452: * Loads from file placed in src/dncomponents.properties
453: *
454: */
455: private static void loadOrdering() {
456: // Read the file to an array of lines
457: String line;
458: LinkedHashMap map = new LinkedHashMap();
459: BufferedReader in = null;
460: InputStreamReader inf = null;
461: try {
462: InputStream is = obj.getClass().getResourceAsStream(
463: "/dncomponents.properties");
464: //log.info("is is: " + is);
465: if (is != null) {
466: inf = new InputStreamReader(is);
467: //inf = new FileReader("c:\\foo.properties");
468: in = new BufferedReader(inf);
469: if (!in.ready())
470: throw new IOException();
471: String[] splits = null;
472: while ((line = in.readLine()) != null) {
473: if (!line.startsWith("#")) { // # is a comment line
474: splits = StringUtils.split(line, '=');
475: if ((splits != null) && (splits.length > 1)) {
476: String name = splits[0];
477: DERObjectIdentifier oid = new DERObjectIdentifier(
478: splits[1]);
479: map.put(name, oid);
480: }
481: }
482: }
483: in.close();
484: // Now we have read it in, transfer it to the main oid map
485: log.info("Using DN components from properties file");
486: oids.clear();
487: oids.putAll(map);
488: Set keys = map.keySet();
489: // Set the maps to the desired ordering
490: dNObjectsForward = (String[]) keys
491: .toArray(new String[0]);
492: } else {
493: log.debug("Using default values for DN components");
494: }
495: } catch (IOException e) {
496: log.debug("Using default values for DN components");
497: } finally {
498: try {
499: if (inf != null)
500: inf.close();
501: if (in != null)
502: in.close();
503: } catch (IOException e) {
504: }
505: }
506:
507: }
508:
509: }
|