Clover Coverage Report - Atlassian Trusted Apps(Aggregated)
Coverage timestamp: Tue Jun 9 2009 19:34:44 CDT
70   333   34   5.83
10   249   0.49   1.71
12     2.83  
7    
 
 
  BouncyCastleEncryptionProvider       Line # 34 54 25 100% 1.0
  BouncyCastleEncryptionProvider.SecretKeyFactory       Line # 253 0 0 - -1.0
  BouncyCastleEncryptionProvider.BCKeyFactory       Line # 258 2 2 100% 1.0
  BouncyCastleEncryptionProvider.ValidatingSecretKeyFactory       Line # 275 6 3 100% 1.0
  BouncyCastleEncryptionProvider.SecretKeyValidator       Line # 300 0 0 - -1.0
  BouncyCastleEncryptionProvider.TransmissionValidator       Line # 308 6 3 100% 1.0
  BouncyCastleEncryptionProvider.IllegalKeyException       Line # 325 2 1 0% 0.0
 
  (34)
 
1    package com.atlassian.security.auth.trustedapps;
2   
3    import com.atlassian.security.auth.trustedapps.Transcoder.Base64Transcoder;
4   
5    import org.bouncycastle.jce.provider.BouncyCastleProvider;
6   
7    import java.io.BufferedReader;
8    import java.io.ByteArrayInputStream;
9    import java.io.CharConversionException;
10    import java.io.IOException;
11    import java.io.InputStreamReader;
12    import java.io.StringWriter;
13    import java.security.InvalidKeyException;
14    import java.security.KeyFactory;
15    import java.security.KeyPair;
16    import java.security.KeyPairGenerator;
17    import java.security.NoSuchAlgorithmException;
18    import java.security.NoSuchProviderException;
19    import java.security.PrivateKey;
20    import java.security.Provider;
21    import java.security.PublicKey;
22    import java.security.spec.InvalidKeySpecException;
23    import java.security.spec.PKCS8EncodedKeySpec;
24    import java.security.spec.X509EncodedKeySpec;
25   
26    import javax.crypto.BadPaddingException;
27    import javax.crypto.Cipher;
28    import javax.crypto.IllegalBlockSizeException;
29    import javax.crypto.KeyGenerator;
30    import javax.crypto.NoSuchPaddingException;
31    import javax.crypto.SecretKey;
32    import javax.crypto.spec.SecretKeySpec;
33   
 
34    public class BouncyCastleEncryptionProvider extends BaseEncryptionProvider
35    {
36    private static final Provider PROVIDER = new BouncyCastleProvider();
37   
38    private static final String STREAM_CIPHER = "RC4";
39    private static final String ASYM_CIPHER = "RSA/NONE/NoPadding";
40    private static final String ASYM_ALGORITHM = "RSA";
41   
42    private final SecretKeyFactory secretKeyFactory;
43    private final Transcoder transcoder;
44   
 
45  52 toggle public BouncyCastleEncryptionProvider()
46    {
47  52 this(new ValidatingSecretKeyFactory(new BCKeyFactory(), new TransmissionValidator()), new Base64Transcoder());
48    }
49   
 
50  52 toggle private BouncyCastleEncryptionProvider(SecretKeyFactory secretKeyFactory, Transcoder transcoder)
51    {
52  52 Null.not("secretKeyFactory", secretKeyFactory);
53  52 Null.not("transcoder", transcoder);
54   
55  52 this.secretKeyFactory = secretKeyFactory;
56  52 this.transcoder = transcoder;
57    }
58   
59    /**
60    * Decodes the given form into the real key object according to the given algorithm Uses Bouncy Castle as a provider
61    *
62    * @param encodedForm
63    * the byte[] containing the key data
64    * @return the generated PublicKey
65    * @throws NoSuchAlgorithmException
66    * @throws InvalidKeySpecException
67    * @throws NoSuchProviderException
68    */
 
69  3 toggle public PublicKey toPublicKey(byte[] encodedForm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
70    {
71  3 final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedForm);
72  3 final KeyFactory keyFactory = KeyFactory.getInstance(ASYM_ALGORITHM, PROVIDER);
73  3 return keyFactory.generatePublic(pubKeySpec);
74    }
75   
76    /**
77    * Decodes the given form into the real key object according to the given algorithm Uses Bouncy Castle as a provider
78    *
79    * @param encodedForm
80    * the PKS8 encoded key data
81    * @return a fully formed PrivateKey
82    * @throws NoSuchAlgorithmException
83    * @throws InvalidKeySpecException
84    * @throws NoSuchProviderException
85    */
 
86  2 toggle public PrivateKey toPrivateKey(byte[] encodedForm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
87    {
88  2 final PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(encodedForm);
89  2 final KeyFactory keyFactory = KeyFactory.getInstance(ASYM_ALGORITHM, PROVIDER);
90  2 return keyFactory.generatePrivate(pubKeySpec);
91    }
92   
93    /**
94    * Generates a new KeyPair.
95    * <p>
96    * Given algorithm name will be used to generate the key pair. It is mandatory. Security provides parameter is
97    * optional and can be null in which case the choice of a provider is left to the VM. Key size is optional and can
98    * be set to -1 in which case the default size is used.
99    *
100    * @throws NoSuchAlgorithmException
101    * @throws NoSuchProviderException
102    */
 
103  17 toggle public KeyPair generateNewKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException
104    {
105  17 final KeyPairGenerator gen = KeyPairGenerator.getInstance(ASYM_ALGORITHM, PROVIDER);
106  17 return gen.generateKeyPair();
107    }
108   
 
109  22 toggle public ApplicationCertificate decodeEncryptedCertificate(EncryptedCertificate encCert, PublicKey publicKey, String appId) throws InvalidCertificateException
110    {
111  22 final BufferedReader in;
112  22 try
113    {
114  22 final Cipher asymCipher = Cipher.getInstance(ASYM_CIPHER, PROVIDER);
115  22 asymCipher.init(Cipher.DECRYPT_MODE, publicKey);
116   
117    /**
118    * this should only happen with protocol version#1 or greater
119    */
120  22 final String encryptedMagicNumber = encCert.getMagicNumber();
121  22 if (encryptedMagicNumber != null)
122    {
123  18 final String magicNumber = new String(asymCipher.doFinal(transcoder.decode(encryptedMagicNumber)), TrustedApplicationUtils.Constant.CHARSET_NAME);
124  18 TrustedApplicationUtils.validateMagicNumber("public key", appId, encCert.getProtocolVersion(), magicNumber);
125    }
126  4 else if (encCert.getProtocolVersion() != null)
127    {
128  1 throw new InvalidCertificateException(new TransportErrorMessage.BadMagicNumber("public key", appId));
129    }
130   
131  19 final byte[] secretKeyData = asymCipher.doFinal(transcoder.decode(encCert.getSecretKey()));
132  19 final SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyData, STREAM_CIPHER);
133   
134  19 final Cipher symCipher = Cipher.getInstance(STREAM_CIPHER, PROVIDER);
135  19 symCipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
136  17 final byte[] decryptedData = symCipher.doFinal(transcoder.decode(encCert.getCertificate()));
137  17 in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(decryptedData), TrustedApplicationUtils.Constant.CHARSET_NAME));
138    }
139    // ///CLOVER:OFF
140    catch (NoSuchAlgorithmException e)
141    {
142    throw new AssertionError(e);
143    }
144    catch (NoSuchPaddingException e)
145    {
146    throw new AssertionError(e);
147    }
148    catch (NumberFormatException e)
149    {
150    throw new SystemException(appId, e);
151    }
152    catch (IllegalBlockSizeException e)
153    {
154    throw new SystemException(appId, e);
155    }
156    catch (BadPaddingException e)
157    {
158    throw new SystemException(appId, e);
159    }
160    catch (InvalidKeyException e)
161    {
162    throw new InvalidCertificateException(new TransportErrorMessage.BadMagicNumber("secret key", appId));
163    }
164    catch (SecurityException e)
165    {
166    // this is here for Java 1.4 only where this exception is thrown when a bad secret key is encountered
167    throw new InvalidCertificateException(new TransportErrorMessage.BadMagicNumber("secret key", appId));
168    }
169    catch (IOException e)
170    {
171    throw new RuntimeException(e);
172    }
173    // /CLOVER:ON
174   
175  17 try
176    {
177  17 final String created = in.readLine();
178  17 final String userName = in.readLine();
179    // validate the magic number before trying to parse the timestamp
180  17 TrustedApplicationUtils.validateMagicNumber("secret key", appId, encCert.getProtocolVersion(), in.readLine());
181  15 final long timeCreated = Long.parseLong(created);
182   
183  14 return new DefaultApplicationCertificate(appId, userName, timeCreated);
184    }
185    catch (NumberFormatException e)
186    {
187  1 throw new SystemException(appId, e);
188    }
189    // ///CLOVER:OFF
190    catch (CharConversionException e)
191    {
192    // only thrown under IBM JDK when unsupported utf8 chars are encountered
193    throw new SystemException(appId, e);
194    }
195    catch (IOException e)
196    {
197    throw new RuntimeException(e);
198    }
199    // /CLOVER:ON
200    }
201   
 
202  13 toggle public EncryptedCertificate createEncryptedCertificate(String userName, PrivateKey privateKey, String appId)
203    {
204  13 try
205    {
206  13 final SecretKey secretKey = secretKeyFactory.generateSecretKey();
207  13 final Cipher symmetricCipher = Cipher.getInstance(STREAM_CIPHER, PROVIDER);
208  13 symmetricCipher.init(Cipher.ENCRYPT_MODE, secretKey);
209   
210  13 final Cipher asymCipher = Cipher.getInstance(ASYM_CIPHER, PROVIDER);
211  13 asymCipher.init(Cipher.ENCRYPT_MODE, privateKey);
212   
213  13 final String encryptedKey = transcoder.encode(asymCipher.doFinal(secretKey.getEncoded()));
214  13 final String encryptedMagic = transcoder.encode(asymCipher.doFinal(transcoder.getBytes(TrustedApplicationUtils.Constant.MAGIC)));
215   
216  13 final StringWriter writer = new StringWriter();
217  13 writer.write(String.valueOf(System.currentTimeMillis()));
218  13 writer.write('\n');
219  13 writer.write(userName);
220  13 writer.write('\n');
221  13 writer.write(TrustedApplicationUtils.Constant.MAGIC);
222  13 writer.flush();
223  13 final byte[] encryptedData = symmetricCipher.doFinal(transcoder.getBytes(writer.toString()));
224  13 final String encodedData = transcoder.encode(encryptedData);
225   
226  13 return new DefaultEncryptedCertificate(appId, encryptedKey, encodedData, TrustedApplicationUtils.Constant.VERSION, encryptedMagic);
227    }
228    // ///CLOVER:OFF
229    catch (NoSuchAlgorithmException e)
230    {
231    throw new AssertionError(e);
232    }
233    catch (NoSuchPaddingException e)
234    {
235    throw new AssertionError(e);
236    }
237    catch (InvalidKeyException e)
238    {
239    // only thrown under some JDKs, make it consistent
240    throw new IllegalKeyException(e);
241    }
242    catch (IllegalBlockSizeException e)
243    {
244    throw new IllegalKeyException(e);
245    }
246    catch (BadPaddingException e)
247    {
248    throw new IllegalKeyException(e);
249    }
250    // /CLOVER:ON
251    }
252   
 
253    interface SecretKeyFactory
254    {
255    SecretKey generateSecretKey();
256    }
257   
 
258    static class BCKeyFactory implements SecretKeyFactory
259    {
 
260  14 toggle public SecretKey generateSecretKey()
261    {
262  14 try
263    {
264  14 return KeyGenerator.getInstance(STREAM_CIPHER, PROVIDER).generateKey();
265    }
266    // /CLOVER:OFF
267    catch (NoSuchAlgorithmException e)
268    {
269    throw new AssertionError(e);
270    }
271    // /CLOVER:ON
272    }
273    }
274   
 
275    static class ValidatingSecretKeyFactory implements SecretKeyFactory
276    {
277    private final SecretKeyFactory delegate;
278    private final SecretKeyValidator validator;
279   
 
280  53 toggle ValidatingSecretKeyFactory(SecretKeyFactory secretKeyFactory, SecretKeyValidator validator)
281    {
282  53 this.delegate = secretKeyFactory;
283  53 this.validator = validator;
284    }
285   
 
286  14 toggle public SecretKey generateSecretKey()
287    {
288  14 SecretKey result = delegate.generateSecretKey();
289  18 while (!validator.isValid(result))
290    {
291  4 result = delegate.generateSecretKey();
292    }
293  14 return result;
294    }
295    }
296   
297    /**
298    * check that a secret key is valid
299    */
 
300    interface SecretKeyValidator
301    {
302    boolean isValid(SecretKey secretKey);
303    }
304   
305    /**
306    * leading zero's in the sevret key byte array lead to transmission problems
307    */
 
308    static class TransmissionValidator implements SecretKeyValidator
309    {
 
310  17 toggle public boolean isValid(SecretKey secretKey)
311    {
312  17 final byte[] encoded = secretKey.getEncoded();
313  17 if (encoded.length != 16)
314    {
315  2 return false;
316    }
317  15 if (encoded[0] == 0)
318    {
319  1 return false;
320    }
321  14 return true;
322    }
323    }
324   
 
325    static class IllegalKeyException extends IllegalArgumentException
326    {
 
327  0 toggle IllegalKeyException(Exception ex)
328    {
329  0 super(ex.toString());
330  0 this.initCause(ex);
331    }
332    }
333    }