001: //
002: // This file is part of the prose package.
003: //
004: // The contents of this file are subject to the Mozilla Public License
005: // Version 1.1 (the "License"); you may not use this file except in
006: // compliance with the License. You may obtain a copy of the License at
007: // http://www.mozilla.org/MPL/
008: //
009: // Software distributed under the License is distributed on an "AS IS" basis,
010: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: // for the specific language governing rights and limitations under the
012: // License.
013: //
014: // The Original Code is prose.
015: //
016: // The Initial Developer of the Original Code is Andrei Popovici. Portions
017: // created by Andrei Popovici are Copyright (C) 2002 Andrei Popovici.
018: // All Rights Reserved.
019: //
020: // Contributor(s):
021: // $Id: SignedAspect.java,v 1.1.1.1 2003/07/02 15:30:51 apopovic Exp $
022: // =====================================================================
023: //
024: // (history at end)
025: //
026:
027: package ch.ethz.prose;
028:
029: // used packages
030: import java.io.FileInputStream;
031: import java.io.IOException;
032: import java.rmi.MarshalledObject;
033: import java.security.InvalidKeyException;
034: import java.security.KeyPair;
035: import java.security.KeyStore;
036: import java.security.NoSuchAlgorithmException;
037: import java.security.PrivateKey;
038: import java.security.PublicKey;
039: import java.security.Signature;
040: import java.security.SignatureException;
041: import java.security.SignedObject;
042: import java.util.List;
043:
044: import ch.ethz.prose.crosscut.Crosscut;
045: import ch.ethz.inf.util.Logger;
046:
047: /**
048: * Class SignedAspect is a wrapper for an Aspect contained in a
049: * SignedObject. Thus it is possible to verify the signer of the
050: * extension before it is used. For security reason, this class
051: * is (and has to be) declared <code>final</code><p>
052: *
053: * At construction, the public and private keys passed as <code>
054: * KeyPair</code> are not checked for being a valid pair
055: * and the private key is not stored for later use. Only
056: * an explicit call to <code>verifyExtension</code> does
057: * check the public key stored. The signing algorithm
058: * to use is deduced by the algorithm type of the public
059: * key: if the public key is of type "DSA" then the algorithm
060: * "SHA1withDSA" is used, in case of an "RSA" key it is "MD5withRSA".
061: * Keys generated with the <code>keytool</code> for the standard
062: * Java 2 keystore are of type "DSA" if not specified otherwise.
063: * The creation of certificates has to be performed by a third party
064: * tool, such as the ones provided with OpenSSL.<p>
065: *
066: * The JCE signing engine that is used can not be specified for the
067: * moment, this class simply uses the first available one.<p>
068: *
069: * To repeat: It is up to the user of this class to assign trust to such
070: * an extension, this class can only be used to transfer an extension
071: * secured from unnoticed changes.<p>
072: *
073: * Remark: to simplify the security auditing of this class, the
074: * necessary and constant parameters are passed to the constructor
075: * and no additional setter-methods are available.
076: *
077: * @version $Revision: 1.1.1.1 $
078: * @author Marcel Muller
079: */
080: final public class SignedAspect extends Aspect {
081:
082: private static final long serialVersionUID = 3256441395777779248L;
083: private static KeyPair keyPair;
084:
085: private SignedObject signedExtension;
086: private PublicKey publicKey;
087: // private Certificate[] certificateChain; // will work only with jdk 1.3 and later
088:
089: transient Aspect wrappedExtension;
090: transient boolean verified = false;
091:
092: /**
093: * Constructs a signed extension and protects it from being modified unnoticed.
094: * Only the public key from the key pair passed is store in the generated object.
095: *
096: * @param extension the extension to be wrapped into a SignedObject
097: * @param keys the key pair used to sign and verify the extension
098: */
099: public SignedAspect(Aspect extension, KeyPair keys) {
100: publicKey = keys.getPublic();
101:
102: try {
103: Signature signingEngine = Signature
104: .getInstance(getSigningAlgorithm());
105: signedExtension = new SignedObject(new MarshalledObject(
106: extension), keys.getPrivate(), signingEngine);
107: } catch (Exception e) {
108: throw new IllegalArgumentException(
109: "failed to sign extension (" + e + ")");
110: }
111: }
112:
113: // delegate work
114: public void insertionAction(boolean beforeInsertion)
115: throws AspectInsertionException {
116: getExtension().insertionAction(beforeInsertion);
117: }
118:
119: public void withdrawalAction(boolean beforeWithdrawal) {
120: getExtension().withdrawalAction(beforeWithdrawal);
121: }
122:
123: protected Crosscut[] crosscuts() {
124: return getExtension().crosscuts();
125: }
126:
127: public List getCrosscuts() {
128: return getExtension().getCrosscuts();
129: }
130:
131: /**
132: * Convinience method to sign extensions. Throws IllegalStateException
133: * if signing is not possible.<p>
134: *
135: * FIX: CONTAINS A LOT OF HARD CODED STUFF!!!!
136: */
137: public static SignedAspect signExtension(Aspect ext) {
138: try {
139: if (keyPair == null) {
140: String ksl = System
141: .getProperty("ch.ethz.prose.keystore.location");
142: String ksp = System
143: .getProperty("ch.ethz.prose.keystore.password");
144: String alias = "runes-system"; // FIX
145:
146: KeyStore ks = KeyStore.getInstance("JKS");
147: ks.load(new FileInputStream(ksl), ksp.toCharArray());
148: PrivateKey privateKey = (PrivateKey) ks.getKey(alias,
149: alias.toCharArray()); // FIX, password
150: PublicKey publicKey = ks.getCertificate(alias)
151: .getPublicKey();
152: keyPair = new KeyPair(publicKey, privateKey);
153: }
154:
155: return new SignedAspect(ext, keyPair);
156:
157: } catch (Exception e) {
158: Logger
159: .error(
160: "SignedAspect.signExtension: could not sign extension",
161: e);
162: throw new IllegalStateException("could not sign extension");
163: }
164: }
165:
166: /**
167: * Verifies that the public key stored in this object corresponds
168: * to the private key used to sign the extension.
169: *
170: * @throws NoSuchAlgorithmException
171: * @throws SignatureException
172: * @throws InvalidKeyException
173: */
174: public void verifyExtension() throws NoSuchAlgorithmException,
175: SignatureException, InvalidKeyException {
176: Signature verificationEngine = Signature
177: .getInstance(getSigningAlgorithm());
178: signedExtension.verify(publicKey, verificationEngine);
179: verified = true;
180: }
181:
182: /**
183: * Returns wrapped extension without checking it for being signed
184: * by a key corresponding to the public key stored.
185: *
186: * @return the wrapped extension
187: */
188: public Aspect getExtension() {
189: try {
190: if (wrappedExtension == null) {
191: wrappedExtension = (Aspect) ((MarshalledObject) signedExtension
192: .getObject()).get();
193: }
194:
195: return wrappedExtension;
196:
197: } catch (IOException e) {
198: Logger.error("SignedAspect.getExtension: io exception", e);
199: throw new RuntimeException("contained exception " + e);
200:
201: } catch (ClassNotFoundException e) {
202: Logger.error("SignedAspect.getExtension: class not found",
203: e);
204: throw new RuntimeException("contained exception " + e);
205: }
206: }
207:
208: /**
209: * Returns public key that possibly corresponds to signing key
210: *
211: * @return the public key passed in the constructor
212: */
213: public PublicKey getPublicKey() {
214: return publicKey;
215: }
216:
217: /**
218: * Returns signing algorithm name
219: *
220: * @return the algorithm name
221: */
222: public String getSigningAlgorithm() {
223: String signingAlgorithm;
224:
225: if (publicKey.getAlgorithm().equals("DSA")) {
226: signingAlgorithm = "SHA1withDSA";
227:
228: } else if (publicKey.getAlgorithm().equals("RSA")) {
229: signingAlgorithm = "MD5withRSA";
230:
231: } else {
232: throw new IllegalArgumentException(
233: "unknown key algorithm, currently supported are DSA and RSA");
234: }
235:
236: return signingAlgorithm;
237: }
238:
239: /**
240: * Implementation: we don't use getExtension() here, as we don't want to
241: * de-marshall extension just because of the toString call
242: */
243: public String toString() {
244: if (wrappedExtension != null) {
245: return "SignedAspect, "
246: + (verified ? "verified" : "not verified")
247: + ", containing " + wrappedExtension.toString();
248:
249: } else {
250: return "SignedAspect, not accessed";
251: }
252: }
253:
254: // public void setCertificateChain(Certificate[] certificateChain) {
255: // this.certificateChain = certificateChain;
256: // }
257:
258: // public Certificate[] getCertificateChain() {
259: // return certificateChain;
260: // }
261:
262: /**
263: * Delegate to wrapped extension
264: */
265: public boolean equals(Object o) {
266: if (o instanceof SignedAspect) {
267: return getExtension().equals(
268: ((SignedAspect) o).getExtension());
269:
270: } else if (o instanceof Aspect) {
271: return getExtension().equals(o);
272:
273: } else {
274: return false;
275: }
276: }
277:
278: /**
279: * delegate to wrapped extension
280: */
281: public int hashCode() {
282: return getExtension().hashCode();
283: }
284: }
285:
286: //======================================================================
287: //
288: // $Log: SignedAspect.java,v $
289: // Revision 1.1.1.1 2003/07/02 15:30:51 apopovic
290: // Imported from ETH Zurich
291: //
292: // Revision 1.1 2003/05/05 13:58:30 popovici
293: // renaming from runes to prose
294: //
295: // Revision 1.2 2003/04/17 15:43:44 popovici
296: // crosscuts renamed to 'getCrosscuts'
297: // createCrosscuts renamed to 'crosscuts'
298: //
299: // Revision 1.1 2003/04/17 15:15:04 popovici
300: // Extension->Aspect renaming
301: //
302: // Revision 1.6 2003/04/17 12:49:41 popovici
303: // Refactoring of the crosscut package
304: // ExceptionCut renamed to ThrowCut
305: // McutSignature is now SignaturePattern
306: //
307: // Revision 1.5 2003/04/17 08:47:14 popovici
308: // Important functionality additions
309: // - Cflow specializers
310: // - Restructuring of the MethodCut, SetCut, ThrowCut, and GetCut (they are much smaller)
311: // - Transactional capabilities
312: // - Total refactoring of Specializer evaluation, which permits fine-grained distinction
313: // between static and dynamic specializers.
314: // - Functionality pulled up in abstract classes
315: // - Uniformization of advice methods patterns and names
316: //
317: // Revision 1.4 2003/03/04 18:36:37 popovici
318: // Organization of imprts
319: //
320: // Revision 1.3 2002/05/29 13:39:06 popovici
321: // minor double semicolumn eliminated
322: //
323: // Revision 1.2 2002/03/28 13:48:38 popovici
324: // Mozilla-ified
325: //
326: // Revision 1.1.1.1 2001/11/29 18:13:16 popovici
327: // Sources from runes
328: //
329: // Revision 1.1.2.6 2001/04/27 07:09:28 mrmuller
330: // Added UGLY convenience method to sign extensions. Like
331: // this, the ugly code is kept centrally and not spread
332: // all around the project.
333: //
334: // Revision 1.1.2.5 2001/04/21 13:57:08 mrmuller
335: // correct handling of equals and hashcode. this method calls
336: // are now correctly delegated to the extension contained inside
337: //
338: // Revision 1.1.2.4 2001/04/10 11:17:30 mrmuller
339: // removed passing of verification engine as parameter and added JavaDoc
340: //
341: // Revision 1.1.2.3 2001/03/27 18:25:50 mrmuller
342: // introduced getSigningAlgorithm
343: //
344: // Revision 1.1.2.2 2001/03/16 17:29:19 mrmuller
345: // cosmetics, javadoc and exception handling changes
346: //
347: // Revision 1.1.2.1 2001/03/16 13:12:32 mrmuller
348: // initial release of new (really) secure extension insertion mechanism
349: //
|