View Javadoc

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 }