1 package org.astrogrid.security;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7 import java.security.PrivateKey;
8 import java.security.cert.CertificateException;
9 import java.security.cert.CertificateFactory;
10 import java.security.cert.CertPath;
11 import java.security.cert.X509Certificate;
12 import javax.security.auth.Subject;
13 import javax.security.auth.x500.X500Principal;
14
15
16 /***
17 * A container for security information.
18 *
19 * This is a Java-bean class in which the properties are various
20 * credentials and/or principals for secured messaging. It is a standard way,
21 * within its package, of passing security information within the same JVM.
22 *
23 * Applications may use this class directly, but are more likely to
24 * use one of the sub-classes that deal with messaging systems.
25 *
26 * "Principals" are identities that have been authenticated using
27 * credentials. Until authentication succeeds, a SecurityGuard contains
28 * no principals. An object representing an identity may be stored as
29 * a credential before authentication and as both a credential and a
30 * principal after authentication.
31 *
32 * In the current implementation, the information is stored in a JAAS
33 * Subject. Special accessors are provided for certain kinds of principals
34 * and credentials. A general accessor is also available to retrieve the
35 * entire Subject, and this serves where dedicated accessors are not yet
36 * available. However, this general accessor may be later be protected
37 * against use from outside the current package; application code should
38 * use the specialized accessors where they are available.
39 *
40 * @author Guy Rixon
41 */
42 public class SecurityGuard {
43
44 /***
45 * The JAAS Subject for all credentials and principals.
46 */
47 protected Subject subject;
48
49 /***
50 * Constructs a SecurityGuard with empty
51 * JAAS subjects.
52 */
53 public SecurityGuard () {
54 this.subject = new Subject();
55 }
56
57 /***
58 * Constructs a SecurityGuard with a
59 * given JAAS subject for grid credentials.
60 * No SSO credentials are set.
61 */
62 public SecurityGuard (Subject s) {
63 this.subject = this.cloneSubject(s);
64 }
65
66 /***
67 * Creates a SecurityGuard with credentials.
68 * The credentials are copied from a given SecurityGuard.
69 *
70 * @param sg The source of the credentials.
71 */
72 public SecurityGuard (SecurityGuard sg) {
73 this.subject = this.cloneSubject(sg.getSubject());
74 }
75
76
77 /***
78 * Retrieves the entire JAAS subject.
79 * Software outside the org.astrogrid.security package
80 * should avoid this method.
81 *
82 * @return - the subject (never null).
83 */
84 public Subject getSubject() {
85 return this.subject;
86 }
87
88 /***
89 * Retrieves the entire JAAS subject.
90 * Provided only for backward compatibility with the workbench.
91 *
92 * @return - the subject (never null).
93 * @deprecated - Use {@link getSubject} instead.
94 */
95 public Subject getSsoSubject() {
96 return this.subject;
97 }
98
99 /***
100 * Retrieves the entire JAAS subject.
101 * Provided only for backward compatibility with the workbench.
102 *
103 * @return - the subject (never null).
104 * @deprecated - Use {@link getSubject} instead.
105 */
106 public Subject getGridSubject() {
107 return this.subject;
108 }
109
110 /***
111 * Sets the account name credential for single sign-on.
112 * This account name is used when the user first
113 * signs on to the grid. It may be different to the
114 * account name used in authenticating to services
115 * within the grid.
116 *
117 * @param name The account name.
118 */
119 public void setSsoUsername (String name) {
120 AccountName account = new AccountName(name);
121 this.subject.getPublicCredentials().add(account);
122 }
123
124 /***
125 * Retrieves the account name credential for single sign on.
126 * This account name is used when the user first
127 * signs on to the grid. It may be different to the
128 * account name used in authenticating to services
129 * within the grid.
130 *
131 * @return the account name
132 */
133 public String getSsoUsername () {
134 Set names = this.subject.getPublicCredentials(AccountName.class);
135 return (names.size() > 0)? ((AccountName)(names.iterator().next())).getName() : null;
136 }
137
138 /***
139 * Sets the password for single sign-on.
140 * This password is used when the user first
141 * signs on to the grid. It may be different to the
142 * account name used in authenticating to services
143 * within the grid.
144 *
145 * @param word the password
146 */
147 public void setSsoPassword (String word) {
148 this.subject.getPrivateCredentials().add(word);
149 }
150
151 /***
152 * Retrieves the password for single sign-on.
153 * This password is used when the user first
154 * signs on to the grid. It may be different to the
155 * account name used in authenticating to services
156 * within the grid.
157 *
158 * @return the password
159 */
160 public String getSsoPassword () {
161 Set passwords = this.subject.getPrivateCredentials(String.class);
162 if (passwords.size() == 0) {
163 return null;
164 }
165 else {
166 return (String)(passwords.iterator().next());
167 }
168 }
169
170 /***
171 * Retrieves the X500 distinguished name.
172 *
173 * @return - the name (null if not authenticated by signature).
174 */
175 public X500Principal getX500Principal() {
176 Set principals = this.subject.getPrincipals(X500Principal.class);
177 return (principals.size() > 0)? (X500Principal)(principals.iterator().next()) : null;
178 }
179
180 /***
181 * Records the X500 distinguished name which has been authenticated.
182 *
183 * @return - the name (null if not authenticated by signature).
184 */
185 public void setX500Principal(X500Principal p) {
186 this.subject.getPrincipals().add(p);
187 }
188
189 /***
190 * Gets the certificate-chain public-credential. This is
191 * expressed as an array of X.509 certificates ordered such that
192 * the signature on certificate i may be checked using the public key
193 * in certificate i+1.
194 *
195 * @return - the chain (never null; zero-length array if no certificates).
196 */
197 public X509Certificate[] getCertificateChain() {
198 Set s = this.subject.getPublicCredentials(CertPath.class);
199 CertPath p = (CertPath)(s.iterator().next());
200 List l = p.getCertificates();
201 X509Certificate[] chain = new X509Certificate[l.size()];
202 for (int i = 0; i < chain.length; i++) {
203 chain[i] = (X509Certificate)(l.get(i));
204 }
205 return chain;
206 }
207
208 /***
209 * Sets the certificate-chain public-credential. This is
210 * expressed as an array of X.509 certificates ordered such that
211 * the signature on certificate i may be checked using the public key
212 * in certificate i+1.
213 *
214 * @param chain - the chain (never null; zero-length array if no certificates).
215 * @throws CertificateException - if the JRE does not support X.509.
216 */
217 public void setCertificateChain(X509Certificate[] chain) throws CertificateException {
218 CertificateFactory f = CertificateFactory.getInstance("X509");
219 List l = new ArrayList(chain.length);
220 for (int i = 0; i < chain.length; i++) {
221 assert chain[i] != null;
222 l.add(chain[i]);
223 }
224 CertPath p = f.generateCertPath(l);
225 this.subject.getPublicCredentials().add(p);
226 }
227
228 /***
229 * Retreives the private key for signing messages.
230 *
231 * @return - the key (null if no key is present).
232 */
233 public PrivateKey getPrivateKey() {
234 Set s = this.subject.getPrivateCredentials(PrivateKey.class);
235 return (s.size() > 0)? (PrivateKey)(s.iterator().next()) : null;
236 }
237
238 /***
239 * Defines the private key for signing messages. If a private key
240 * was previously set, then it is replaced by this key. Setting a
241 * null key removes the previous key.
242 *
243 * @param newKey - the new key.
244 */
245 public void setPrivateKey(PrivateKey newKey) {
246 PrivateKey oldKey = this.getPrivateKey();
247 if (oldKey != null) {
248 this.subject.getPrivateCredentials().remove(oldKey);
249 }
250 if (newKey != null) {
251 this.subject.getPrivateCredentials().add(newKey);
252 }
253 }
254
255 /***
256 * Retrieves the X.509 certificate carrying the authenticated identity.
257 * If the caller has been authenticated using a chain of certificates that
258 * includes limited or impersonation proxy certificates, the identity
259 * certificate is the first non-proxy certificate in the chain.
260 */
261 public X509Certificate getIdentityCertificate() {
262 Set s = this.subject.getPublicCredentials(X509Certificate.class);
263 return (s.size() > 0)? (X509Certificate)(s.iterator().next()) : null;
264 }
265
266 /***
267 * Records the X.509 certificate carrying the authenticated identity.
268 * If the caller has been authenticated using a chain of certificates that
269 * includes limited or impersonation proxy certificates, the identity
270 * certificate is the first non-proxy certificate in the chain.
271 */
272 public void setIdentityCertificate(X509Certificate newCert) {
273 X509Certificate oldCert = this.getIdentityCertificate();
274 if (oldCert != null) {
275 this.subject.getPublicCredentials().remove(oldCert);
276 }
277 this.subject.getPublicCredentials().add(newCert);
278 }
279
280 /***
281 * Extracts the first Principal of a given type.
282 * @param clazz - The class of principal required.
283 * @return The Principal, or null if none of the requested type are present.
284 */
285 public Object getFirstPrincipal(Class clazz) {
286 Object[] a = this.subject.getPrincipals(clazz).toArray();
287 return (a.length > 0)? a[0] : null;
288 }
289
290 /***
291 * Extracts the first private credential of a given type.
292 * @param clazz - The class of credental required.
293 * @return The credential, or null if none of the requested type are present.
294 */
295 public Object getFirstPrivateCredential(Class clazz) {
296 Object[] a = this.subject.getPrivateCredentials(clazz).toArray();
297 return (a.length > 0)? a[0] : null;
298 }
299
300 /***
301 * Copy the contents of a JAAS Subject. A shallow cloning is performed on the
302 * sets of principals and credentials. After the cloning, operations on the
303 * sets in the original do not affect the sets in the copy (and vice versa);
304 * however, operations on the members of those sets in the original affect
305 * the copy (and vice versa). E.g., replacing a PrivateKey in the original
306 * with a new object will not change the key in the copy; but writing to the
307 * contents of a PrivateKey objecct in the orginal changes both original and
308 * copy.
309 *
310 * @param given - the subject to be copied.
311 * @return - the copy.
312 */
313 protected Subject cloneSubject(Subject given) {
314 if (given == null) {
315 return null;
316 }
317 else {
318 return new Subject(false,
319 new HashSet(given.getPrincipals()),
320 new HashSet(given.getPublicCredentials()),
321 new HashSet(given.getPrivateCredentials()));
322 }
323 }
324 }