1 package com.atlassian.security.auth.trustedapps;
2
3 import com.atlassian.security.auth.trustedapps.ApplicationRetriever.ApplicationNotFoundException;
4 import com.atlassian.security.auth.trustedapps.ApplicationRetriever.RemoteSystemNotFoundException;
5
6 import org.bouncycastle.jce.provider.BouncyCastleProvider;
7 import org.bouncycastle.util.encoders.Base64;
8
9 import java.io.File;
10 import java.io.FileWriter;
11 import java.net.URI;
12 import java.net.URL;
13 import java.security.KeyFactory;
14 import java.security.PrivateKey;
15 import java.security.Provider;
16 import java.security.PublicKey;
17 import java.security.spec.PKCS8EncodedKeySpec;
18 import java.security.spec.X509EncodedKeySpec;
19
20 import javax.crypto.Cipher;
21 import javax.crypto.SecretKey;
22 import javax.crypto.spec.SecretKeySpec;
23
24 import junit.framework.TestCase;
25
26 public class TestBouncyCastleEncryptionProvider extends TestCase
27 {
28 private final EncryptionProvider encryptionProvider = new BouncyCastleEncryptionProvider();
29
30 public void testGetApplicationCertificateReturnsEmpty() throws Exception
31 {
32 URL url = this.getClass().getResource("/trustedapps");
33 File root = new File(new URI(url.toString()));
34 assertTrue(root.isDirectory());
35 File cert = new File(root, "admin/appTrustCertificate");
36 cert.delete();
37 File certDir = new File(root, "admin");
38 certDir.mkdirs();
39 assertTrue(cert.getCanonicalPath(), cert.createNewFile());
40 FileWriter writer = new FileWriter(cert);
41 writer.write("");
42 writer.close();
43 try
44 {
45 encryptionProvider.getApplicationCertificate(root.toURI().toString());
46 fail("CertNotFound expected");
47 }
48 catch (ApplicationNotFoundException e)
49 {
50
51 }
52 }
53
54 public void testGetApplicationCertificateNotFoundAtAll() throws Exception
55 {
56 URL url = this.getClass().getResource("/");
57 File root = new File(new URI(url.toString()));
58 assertTrue(root.isDirectory());
59 try
60 {
61 encryptionProvider.getApplicationCertificate(root.toURI().toString());
62 fail("FNFE expected");
63 }
64 catch (ApplicationNotFoundException e)
65 {
66
67 }
68 }
69
70 public void testGetApplicationCertificateMalformedUrl() throws Exception
71 {
72 try
73 {
74 encryptionProvider.getApplicationCertificate("noscheme://some/url");
75 fail("InvalidApplicationDetailsException expected");
76 }
77 catch (RemoteSystemNotFoundException yay)
78 {
79
80 }
81 }
82
83 public void testGetApplicationCertificateReturnsProperly() throws Exception
84 {
85 final String publicKeyData = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu6q2RIBqhT0Ny59W7cZ1zwSHFBAJeCpkbzY0nCMUK5eiim/9TUfNvlpSJj5Ks/zs5Ll4R129rYtTfIfvIq4sSMjXKB8rftswet4uQWdTcTJyesLHbYgHqSIS0b+1JtQsDvjB5aEYApS4nc+fjZmwJQkpN++J8QpqQeoGDzq+zVxAzuGdVrkMEuCXcAS4znYFqW7VytvS0DHhreSIh1fGWLJIqE15Jih91Up2W+dRdLAdW0OVozOsWYFx5+L325PWkSgsDCMGVyNCdTnwCzI4FiYsT4MLODkOnOt9gDQTMjn8H1NM5K3d3Sb7WH2hKwuBxLyDRUY7qUB1bwtGU6LgjQIDAQAB";
86
87 URL url = this.getClass().getResource("/trustedapps");
88 File root = new File(new URI(url.toString()));
89 assertTrue(root.isDirectory());
90 File cert = new File(root, "admin/appTrustCertificate");
91 cert.delete();
92 File certDir = new File(root, "admin");
93 certDir.mkdirs();
94 assertTrue(cert.getCanonicalPath(), cert.createNewFile());
95 FileWriter writer = new FileWriter(cert);
96 writer.write("myApplicationId\n");
97 writer.write(publicKeyData);
98 writer.close();
99 Application app = encryptionProvider.getApplicationCertificate(root.toURI().toString());
100 assertEquals("myApplicationId", app.getID());
101 }
102
103 public void testDecodeBadSecretKey() throws Exception
104 {
105 Token token = new Token();
106 long time = System.currentTimeMillis();
107 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", "bad-secret-key-bad!", token.encrypt("fred", time), TrustedApplicationUtils.Constant.VERSION, token.getMagic());
108 try
109 {
110 encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
111 fail("InvalidCertificateException expected");
112 }
113 catch (InvalidCertificateException e)
114 {
115
116 }
117 }
118
119 public void testDecodeSecretKey() throws Exception
120 {
121 Token token = new Token();
122 long time = System.currentTimeMillis();
123 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), token.encrypt("fred", time), TrustedApplicationUtils.Constant.VERSION, token.getMagic());
124 ApplicationCertificate cert = encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
125 assertEquals(time, cert.getCreationTime().getTime());
126 assertEquals("fred", cert.getUserName());
127 assertEquals("id", cert.getApplicationID());
128 }
129
130 public void testDecodeBadMagicNumber() throws Exception
131 {
132 Token token = new Token();
133 long time = System.currentTimeMillis();
134 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), token.encrypt("fred", time), TrustedApplicationUtils.Constant.VERSION, "bad-magic, bad!");
135 try
136 {
137 encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
138 fail("InvalidCertificateException expected");
139 }
140 catch (InvalidCertificateException e)
141 {
142
143 }
144 }
145
146 public void testDecodeInvalidCertificate() throws Exception
147 {
148 Token token = new Token();
149 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), "TestTrustedApplicationClient.id", TrustedApplicationUtils.Constant.VERSION, token.getMagic());
150 try
151 {
152 encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
153 fail("InvalidCertificateException expected");
154 }
155 catch (InvalidCertificateException e)
156 {
157
158 }
159 }
160
161 public void testDecodeNumberFormatException() throws Exception
162 {
163 Token token = new Token()
164 {
165 String encrypt(String userName, long time)
166 {
167 try
168 {
169 Cipher cipher = Cipher.getInstance(KeyData.STREAM_CIPHER, KeyData.BOUNCY_CASTLE_PROVIDER);
170 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
171 StringBuffer buffer = new StringBuffer("not-a-number").append("\n").append(userName).append("\n").append(TrustedApplicationUtils.Constant.MAGIC);
172 return new String(Base64.encode(cipher.doFinal(buffer.toString().getBytes())), TrustedApplicationUtils.Constant.CHARSET_NAME);
173 }
174 catch (Exception e)
175 {
176 throw new RuntimeException(e);
177 }
178 }
179 };
180 long time = System.currentTimeMillis();
181 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), token.encrypt("fred", time), TrustedApplicationUtils.Constant.VERSION, token.getMagic());
182 try
183 {
184 encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
185 fail("InvalidCertificateException expected");
186 }
187 catch (SystemException e)
188 {
189
190 }
191 }
192
193 public void testDecodeNullMagicNumberVersion0() throws Exception
194 {
195 Token token = new Token();
196 long time = System.currentTimeMillis();
197 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), token.encrypt("fred", time), null, null);
198 ApplicationCertificate cert = encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
199 assertEquals(time, cert.getCreationTime().getTime());
200 assertEquals("fred", cert.getUserName());
201 assertEquals("id", cert.getApplicationID());
202 }
203
204 public void testDecodeNullMagicNumberVersion1() throws Exception
205 {
206 Token token = new Token();
207 long time = System.currentTimeMillis();
208 EncryptedCertificate encrypted = new DefaultEncryptedCertificate("id", token.getSecretKey(), token.encrypt("fred", time), TrustedApplicationUtils.Constant.VERSION, null);
209 try
210 {
211 encryptionProvider.decodeEncryptedCertificate(encrypted, token.getPublicKey(), "id");
212 fail("InvalidCertificateException expected");
213 }
214 catch (InvalidCertificateException e)
215 {
216
217 }
218 }
219
220 public void testBCSecretKeyFactory()
221 {
222 BouncyCastleEncryptionProvider.SecretKeyFactory keyFactory = new BouncyCastleEncryptionProvider.BCKeyFactory();
223 SecretKey secretKey = keyFactory.generateSecretKey();
224 assertNotNull(secretKey);
225 assertEquals("RC4", secretKey.getAlgorithm());
226 assertEquals("RAW", secretKey.getFormat());
227 assertNotNull(secretKey.getEncoded());
228 assertEquals(16, secretKey.getEncoded().length);
229 }
230
231 public void testSecretKeyValidatorValidatesLength16()
232 {
233 BouncyCastleEncryptionProvider.SecretKeyValidator keyValidator = new BouncyCastleEncryptionProvider.TransmissionValidator();
234 SecretKey secretKey = new SecretKeySpec(new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, "ALG");
235 assertTrue(keyValidator.isValid(secretKey));
236 }
237
238 public void testSecretKeyValidatorInValidatesLengthLessThan16()
239 {
240 BouncyCastleEncryptionProvider.SecretKeyValidator keyValidator = new BouncyCastleEncryptionProvider.TransmissionValidator();
241 SecretKey secretKey = new SecretKeySpec(new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, "ALG");
242 assertFalse(keyValidator.isValid(secretKey));
243 }
244
245 public void testSecretKeyValidatorInValidatesLengthGreaterThan16()
246 {
247 BouncyCastleEncryptionProvider.SecretKeyValidator keyValidator = new BouncyCastleEncryptionProvider.TransmissionValidator();
248 SecretKey secretKey = new SecretKeySpec(new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, "ALG");
249 assertFalse(keyValidator.isValid(secretKey));
250 }
251
252 public void testSecretKeyValidatorInValidatesLeadingZero()
253 {
254 BouncyCastleEncryptionProvider.SecretKeyValidator keyValidator = new BouncyCastleEncryptionProvider.TransmissionValidator();
255 SecretKey secretKey = new SecretKeySpec(new byte[] { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, "ALG");
256 assertFalse(keyValidator.isValid(secretKey));
257 }
258
259 public void testValidatingSecretKeyFactory()
260 {
261 final int[] called = new int[2];
262 final int factory = 0;
263 final int validation = 1;
264 final int expectedCallCount = 5;
265
266 BouncyCastleEncryptionProvider.SecretKeyFactory mockFactory = new BouncyCastleEncryptionProvider.SecretKeyFactory()
267 {
268 public SecretKey generateSecretKey()
269 {
270 ++called[factory];
271 return new SecretKeySpec(new byte[] { (byte) called[factory], 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, "ALG");
272 }
273 };
274
275 BouncyCastleEncryptionProvider.SecretKeyValidator mockValidator = new BouncyCastleEncryptionProvider.SecretKeyValidator()
276 {
277 public boolean isValid(SecretKey secretKey)
278 {
279 return ++called[validation] == expectedCallCount;
280 }
281 };
282
283 BouncyCastleEncryptionProvider.SecretKeyFactory testFactory = new BouncyCastleEncryptionProvider.ValidatingSecretKeyFactory(mockFactory, mockValidator);
284 SecretKey secretKey = testFactory.generateSecretKey();
285 assertNotNull(secretKey);
286 assertEquals(expectedCallCount, called[factory]);
287 assertEquals(expectedCallCount, called[validation]);
288 assertEquals(expectedCallCount, called[factory]);
289 assertNotNull(secretKey.getEncoded());
290 assertEquals(16, secretKey.getEncoded().length);
291 assertEquals(expectedCallCount, secretKey.getEncoded()[0]);
292 }
293
294 private static class Token
295 {
296 static final class KeyData
297 {
298 private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCySptbugHAzWUJY3ALWhuSCPhVXnwbUBfsRExYQitBCVny4V1DcU2SAx22bH9dSM0X7NdMObF74r+Wd77QoPAtaySqFLqCeRCbFmhHgVSi+pGeCipTpueefSkz2AX8Aj+9x27tqjBsX1LtNWVLDsinEhBWN68R+iEOmf/6jGWObQIDAQAB";
299 private static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALJKm1u6AcDNZQljcAtaG5II+FVefBtQF+xETFhCK0EJWfLhXUNxTZIDHbZsf11IzRfs10w5sXviv5Z3vtCg8C1rJKoUuoJ5EJsWaEeBVKL6kZ4KKlOm5559KTPYBfwCP73Hbu2qMGxfUu01ZUsOyKcSEFY3rxH6IQ6Z//qMZY5tAgMBAAECgYB4QXJAkFmWXfOEPZnZTlHCUmKN0kkLcx5vsjF8ZkUefNw6wl9Rmh6kGY30+YF+vhf3xzwAoflggjSPnP0LY0Ibf0XxMcNjR1zBsl9X7gKfXghIunS6gbcwrEwBNc5GR4zkYjYaZQ4zVvm3oMS2glV9NlXAUl41VL2XAQC/ENwbUQJBAOdoAz4hZGgke9AxoKLZh215gY+PLXqVLlWf14Ypk70Efk/bVvF10EsAOuAm9queCyr0qNf/vgHrm4HHXwJz4SsCQQDFPXir5qs+Kf2Y0KQ+WO5IRaNmrOlNvWDqJP/tDGfF/TYo6nSI0dGtWNfwZyDB47PbUq3zxCHYjExBJ9vQNZLHAkEA4JlCtHYCl1X52jug1w7c9DN/vc/Q626J909aB3ypSUdoNagFPf0EexcxDcijmDSgUEQA8Qzm5cRBPfg9Tgsc2wJBAIKbiv2hmEFowtHfTvMuJlNbMbF6zF67CaLib0oEDe+QFb4QSqyS69py20MItytM4btYy3GArbzcYl4+y5La9t8CQE2BkMV3MLcpAKjxtK5SYwCyLT591k35isGxmIlSQBQbDmGP9L5ZeXmVGVxRCGbBQjCzeoafPvUZo65kaRQHUJc=";
300
301 private static final String SECRET_KEY = "T52KBiVRRol8V2/DS7cy9G9iRdv+vSH4wLqH9q7wdSiFtZw9MRG3ihhLSmmH1MnkwPeLg+y0wtDZuokMr6eJwPo+1dHO3t2pb7IwVJg+UqGgn97LdihAMgwqLApnQIMquDe5uDuuK6Qaey+D6EXu2E90FI4Z0mHqaE3Wbo+HC50=";
302 private static final String ALGORITHM = "RSA";
303 private static final Provider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
304 private static final String STREAM_CIPHER = "RC4";
305 private static final String ASYM_CIPHER = "RSA/NONE/NoPadding";
306 }
307
308 final PrivateKey privateKey;
309 final PublicKey publicKey;
310 final SecretKey secretKey;
311
312 Token()
313 {
314 try
315 {
316 KeyFactory keyFactory = KeyFactory.getInstance(KeyData.ALGORITHM, KeyData.BOUNCY_CASTLE_PROVIDER);
317 privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(KeyData.PRIVATE_KEY.getBytes(TrustedApplicationUtils.Constant.CHARSET_NAME))));
318 publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decode(KeyData.PUBLIC_KEY.getBytes(TrustedApplicationUtils.Constant.CHARSET_NAME))));
319
320 Cipher cipher = Cipher.getInstance(KeyData.ASYM_CIPHER, KeyData.BOUNCY_CASTLE_PROVIDER);
321 cipher.init(Cipher.DECRYPT_MODE, publicKey);
322
323 byte[] secretKeyData = cipher.doFinal(Base64.decode(KeyData.SECRET_KEY.getBytes(TrustedApplicationUtils.Constant.CHARSET_NAME)));
324
325 secretKey = new SecretKeySpec(secretKeyData, KeyData.STREAM_CIPHER);
326 }
327 catch (Exception e)
328 {
329 throw new RuntimeException(e);
330 }
331 }
332
333 PublicKey getPublicKey()
334 {
335 return publicKey;
336 }
337
338 String getSecretKey()
339 {
340 try
341 {
342 Cipher cipher = Cipher.getInstance(KeyData.ASYM_CIPHER, KeyData.BOUNCY_CASTLE_PROVIDER);
343 cipher.init(Cipher.ENCRYPT_MODE, privateKey);
344 return new String(Base64.encode(cipher.doFinal(secretKey.getEncoded())), TrustedApplicationUtils.Constant.CHARSET_NAME);
345 }
346 catch (Exception e)
347 {
348 throw new RuntimeException(e);
349 }
350 }
351
352 String getMagic()
353 {
354 try
355 {
356 Cipher cipher = Cipher.getInstance(KeyData.ASYM_CIPHER, KeyData.BOUNCY_CASTLE_PROVIDER);
357 cipher.init(Cipher.ENCRYPT_MODE, privateKey);
358 return new String(Base64.encode(cipher.doFinal(TrustedApplicationUtils.Constant.MAGIC.getBytes())), TrustedApplicationUtils.Constant.CHARSET_NAME);
359 }
360 catch (Exception e)
361 {
362 throw new RuntimeException(e);
363 }
364 }
365
366 String encrypt(String userName, long time)
367 {
368 try
369 {
370 Cipher cipher = Cipher.getInstance(KeyData.STREAM_CIPHER, KeyData.BOUNCY_CASTLE_PROVIDER);
371 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
372 StringBuffer buffer = new StringBuffer(String.valueOf(time)).append("\n").append(userName).append("\n").append(TrustedApplicationUtils.Constant.MAGIC);
373 return new String(Base64.encode(cipher.doFinal(buffer.toString().getBytes(TrustedApplicationUtils.Constant.CHARSET_NAME))), TrustedApplicationUtils.Constant.CHARSET_NAME);
374 }
375 catch (Exception e)
376 {
377 throw new RuntimeException(e);
378 }
379 }
380 }
381 }