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 public BouncyCastleEncryptionProvider()
46 {
47 this(new ValidatingSecretKeyFactory(new BCKeyFactory(), new TransmissionValidator()), new Base64Transcoder());
48 }
49
50 private BouncyCastleEncryptionProvider(SecretKeyFactory secretKeyFactory, Transcoder transcoder)
51 {
52 Null.not("secretKeyFactory", secretKeyFactory);
53 Null.not("transcoder", transcoder);
54
55 this.secretKeyFactory = secretKeyFactory;
56 this.transcoder = transcoder;
57 }
58
59
60
61
62
63
64
65
66
67
68
69 public PublicKey toPublicKey(byte[] encodedForm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
70 {
71 final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedForm);
72 final KeyFactory keyFactory = KeyFactory.getInstance(ASYM_ALGORITHM, PROVIDER);
73 return keyFactory.generatePublic(pubKeySpec);
74 }
75
76
77
78
79
80
81
82
83
84
85
86 public PrivateKey toPrivateKey(byte[] encodedForm) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException
87 {
88 final PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(encodedForm);
89 final KeyFactory keyFactory = KeyFactory.getInstance(ASYM_ALGORITHM, PROVIDER);
90 return keyFactory.generatePrivate(pubKeySpec);
91 }
92
93
94
95
96
97
98
99
100
101
102
103 public KeyPair generateNewKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException
104 {
105 final KeyPairGenerator gen = KeyPairGenerator.getInstance(ASYM_ALGORITHM, PROVIDER);
106 return gen.generateKeyPair();
107 }
108
109 public ApplicationCertificate decodeEncryptedCertificate(EncryptedCertificate encCert, PublicKey publicKey, String appId) throws InvalidCertificateException
110 {
111 final BufferedReader in;
112 try
113 {
114 final Cipher asymCipher = Cipher.getInstance(ASYM_CIPHER, PROVIDER);
115 asymCipher.init(Cipher.DECRYPT_MODE, publicKey);
116
117
118
119
120 final String encryptedMagicNumber = encCert.getMagicNumber();
121 if (encryptedMagicNumber != null)
122 {
123 final String magicNumber = new String(asymCipher.doFinal(transcoder.decode(encryptedMagicNumber)), TrustedApplicationUtils.Constant.CHARSET_NAME);
124 TrustedApplicationUtils.validateMagicNumber("public key", appId, encCert.getProtocolVersion(), magicNumber);
125 }
126 else if (encCert.getProtocolVersion() != null)
127 {
128 throw new InvalidCertificateException(new TransportErrorMessage.BadMagicNumber("public key", appId));
129 }
130
131 final byte[] secretKeyData = asymCipher.doFinal(transcoder.decode(encCert.getSecretKey()));
132 final SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyData, STREAM_CIPHER);
133
134 final Cipher symCipher = Cipher.getInstance(STREAM_CIPHER, PROVIDER);
135 symCipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
136 final byte[] decryptedData = symCipher.doFinal(transcoder.decode(encCert.getCertificate()));
137 in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(decryptedData), TrustedApplicationUtils.Constant.CHARSET_NAME));
138 }
139
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
167 throw new InvalidCertificateException(new TransportErrorMessage.BadMagicNumber("secret key", appId));
168 }
169 catch (IOException e)
170 {
171 throw new RuntimeException(e);
172 }
173
174
175 try
176 {
177 final String created = in.readLine();
178 final String userName = in.readLine();
179
180 TrustedApplicationUtils.validateMagicNumber("secret key", appId, encCert.getProtocolVersion(), in.readLine());
181 final long timeCreated = Long.parseLong(created);
182
183 return new DefaultApplicationCertificate(appId, userName, timeCreated);
184 }
185 catch (NumberFormatException e)
186 {
187 throw new SystemException(appId, e);
188 }
189
190 catch (CharConversionException e)
191 {
192
193 throw new SystemException(appId, e);
194 }
195 catch (IOException e)
196 {
197 throw new RuntimeException(e);
198 }
199
200 }
201
202 public EncryptedCertificate createEncryptedCertificate(String userName, PrivateKey privateKey, String appId)
203 {
204 try
205 {
206 final SecretKey secretKey = secretKeyFactory.generateSecretKey();
207 final Cipher symmetricCipher = Cipher.getInstance(STREAM_CIPHER, PROVIDER);
208 symmetricCipher.init(Cipher.ENCRYPT_MODE, secretKey);
209
210 final Cipher asymCipher = Cipher.getInstance(ASYM_CIPHER, PROVIDER);
211 asymCipher.init(Cipher.ENCRYPT_MODE, privateKey);
212
213 final String encryptedKey = transcoder.encode(asymCipher.doFinal(secretKey.getEncoded()));
214 final String encryptedMagic = transcoder.encode(asymCipher.doFinal(transcoder.getBytes(TrustedApplicationUtils.Constant.MAGIC)));
215
216 final StringWriter writer = new StringWriter();
217 writer.write(String.valueOf(System.currentTimeMillis()));
218 writer.write('\n');
219 writer.write(userName);
220 writer.write('\n');
221 writer.write(TrustedApplicationUtils.Constant.MAGIC);
222 writer.flush();
223 final byte[] encryptedData = symmetricCipher.doFinal(transcoder.getBytes(writer.toString()));
224 final String encodedData = transcoder.encode(encryptedData);
225
226 return new DefaultEncryptedCertificate(appId, encryptedKey, encodedData, TrustedApplicationUtils.Constant.VERSION, encryptedMagic);
227 }
228
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
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
251 }
252
253 interface SecretKeyFactory
254 {
255 SecretKey generateSecretKey();
256 }
257
258 static class BCKeyFactory implements SecretKeyFactory
259 {
260 public SecretKey generateSecretKey()
261 {
262 try
263 {
264 return KeyGenerator.getInstance(STREAM_CIPHER, PROVIDER).generateKey();
265 }
266
267 catch (NoSuchAlgorithmException e)
268 {
269 throw new AssertionError(e);
270 }
271
272 }
273 }
274
275 static class ValidatingSecretKeyFactory implements SecretKeyFactory
276 {
277 private final SecretKeyFactory delegate;
278 private final SecretKeyValidator validator;
279
280 ValidatingSecretKeyFactory(SecretKeyFactory secretKeyFactory, SecretKeyValidator validator)
281 {
282 this.delegate = secretKeyFactory;
283 this.validator = validator;
284 }
285
286 public SecretKey generateSecretKey()
287 {
288 SecretKey result = delegate.generateSecretKey();
289 while (!validator.isValid(result))
290 {
291 result = delegate.generateSecretKey();
292 }
293 return result;
294 }
295 }
296
297
298
299
300 interface SecretKeyValidator
301 {
302 boolean isValid(SecretKey secretKey);
303 }
304
305
306
307
308 static class TransmissionValidator implements SecretKeyValidator
309 {
310 public boolean isValid(SecretKey secretKey)
311 {
312 final byte[] encoded = secretKey.getEncoded();
313 if (encoded.length != 16)
314 {
315 return false;
316 }
317 if (encoded[0] == 0)
318 {
319 return false;
320 }
321 return true;
322 }
323 }
324
325 static class IllegalKeyException extends IllegalArgumentException
326 {
327 IllegalKeyException(Exception ex)
328 {
329 super(ex.toString());
330 this.initCause(ex);
331 }
332 }
333 }