View Javadoc

1   /*
2    * The MIT License
3    *
4    * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5    *
6    * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   *
26   * $HeadURL: https://svn.softwareborsen.dk/sosi/trunk/modules/seal/src/main/java/dk/sosi/seal/pki/DistinguishedName.java $
27   * $Id: DistinguishedName.java 8697 2011-09-02 10:33:55Z chg@lakeside.dk $
28   */
29  
30  package dk.sosi.seal.pki;
31  
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Map;
35  import java.util.Set;
36  
37  /**
38   * <p>
39   * Abstraction of X500 names, i.e. sequences of relative DNS
40   * </p>
41   * 
42   * @author thomas@signaturgruppen.dk
43   * @author $LastChangedBy: chg@lakeside.dk $
44   * @version $Revision: 8697 $
45   * @since 1.0
46   */
47  public class DistinguishedName {
48      private static final String COMMONNAME = "cn";
49  
50      private static final String COUNTRY = "c";
51  
52      private static final Map<String, String> oidNameToCanonicalOidName = new HashMap<String, String>();
53  
54      private static final String ORGANIZATION = "o";
55  
56      private static final String ORGANIZATIONALUNIT = "ou";
57  
58      private static final String SERIALNUMBER = "serialNumber";
59  
60      private static final String STATE = "st";
61      static {
62          oidNameToCanonicalOidName.put(COMMONNAME, COMMONNAME);
63          oidNameToCanonicalOidName.put("commonName", COMMONNAME);
64          oidNameToCanonicalOidName.put("CN", COMMONNAME);
65  
66          oidNameToCanonicalOidName.put(ORGANIZATIONALUNIT, ORGANIZATIONALUNIT);
67          oidNameToCanonicalOidName.put("OU", ORGANIZATIONALUNIT);
68  
69          oidNameToCanonicalOidName.put(ORGANIZATION, ORGANIZATION);
70          oidNameToCanonicalOidName.put("O", ORGANIZATION);
71  
72          oidNameToCanonicalOidName.put(SERIALNUMBER, SERIALNUMBER);
73          oidNameToCanonicalOidName.put(SERIALNUMBER.toUpperCase(), SERIALNUMBER);
74          oidNameToCanonicalOidName.put("SN", SERIALNUMBER);
75  
76          oidNameToCanonicalOidName.put(COUNTRY, COUNTRY);
77          oidNameToCanonicalOidName.put("C", COUNTRY);
78  
79          oidNameToCanonicalOidName.put("ST", STATE);
80      }
81  
82      private String distinguishedName;
83  
84      private final Map<String, Set<String>> oidToValue = new HashMap<String, Set<String>>();
85  
86      /**
87       * Construct an instance of <code>DistinguishedName</code>
88       * 
89       * @param distinguishedName
90       *            string representation of DN
91       * @throws PKIException
92       *             if DN parsing fails
93       */
94      public DistinguishedName(String distinguishedName) throws PKIException {
95          super();
96  
97          this.distinguishedName = distinguishedName;
98          parse();
99      }
100 
101     public boolean equals(Object obj) {
102         if(!(obj instanceof DistinguishedName)) {
103             return false; // NOPMD
104         }
105         DistinguishedName other = (DistinguishedName)obj;
106         return other.oidToValue.equals(this.oidToValue);
107     }
108 
109     public String getCommonName() {
110         return getSingleValueByKey(COMMONNAME);
111     }
112 
113     public String getCountry() {
114         return getSingleValueByKey(COUNTRY);
115     }
116 
117     public String getOrganization() {
118         return getSingleValueByKey(ORGANIZATION);
119     }
120 
121     public String getOrganizationalUnit() {
122         return getSingleValueByKey(ORGANIZATIONALUNIT);
123     }
124 
125     public Set<String> getOrganizationalUnits() {
126         return getMultiValueByKey(ORGANIZATIONALUNIT);
127     }
128 
129     public String getSubjectSerialNumber() {
130         return getSingleValueByKey(SERIALNUMBER);
131     }
132 
133     public int hashCode() {
134         return oidToValue.hashCode();
135     }
136 
137     private void addRDN(String name, String value) {
138         String attr = oidNameToCanonicalOidName.get(name);
139         if(oidToValue.containsKey(attr)) {
140             oidToValue.get(attr).add(value);
141         } else {
142             HashSet<String> values = new HashSet<String>();
143             values.add(value);
144             oidToValue.put(attr, values);
145         }
146     }
147 
148     private Set<String> getMultiValueByKey(String key) {
149         Set<String> val = oidToValue.get(key);
150         if(val == null) {
151             return null; // NOPMD
152         }
153         return val;
154     }
155 
156     private String getSingleValueByKey(String key) {
157         Set<String> val = oidToValue.get(key);
158         if(val == null) {
159             return null; // NOPMD
160         }
161         return val.iterator().next();
162     }
163 
164     private void parse() {
165         String[] rdns = distinguishedName.split(",");
166 
167         for (int i = 0; i < rdns.length; i++) {
168             // TODO - repair this with RE, parsing 'CN=Merlin Hughes, O="Baltimore Technologies, Ltd.", ST=Dublin, C=IE'
169             if(rdns[i].indexOf('"') > 0 && !rdns[i].endsWith("\"")) {
170                 rdns[i + 1] = rdns[i] + "," + rdns[i + 1];
171             } else {
172                 String[] fields;
173                 fields = rdns[i].split("[ ]?\\+[ ]?");
174                 if(fields.length > 1) {
175                     for (int j = 0; j < fields.length; j++) {
176                         parseRDN(fields[j]);
177                     }
178                 } else {
179                     parseRDN(rdns[i]);
180                 }
181             }
182         }
183 
184     }
185 
186     private void parseRDN(String rdn) throws PKIException {
187         String[] parts = rdn.split("=");
188         String name = parts[0].trim();
189         String value = parts[1];
190         if(oidNameToCanonicalOidName.get(name) != null) {
191             addRDN(name, value);
192         } else {
193             throw new PKIException("Failed to parse DN: Unknown attribute: " + name);
194         }
195     }
196 }