1   package com.atlassian.seraph.cookie;
2   
3   import com.atlassian.seraph.config.SecurityConfigFactory;
4   import com.atlassian.seraph.util.EncryptionUtils;
5   import org.apache.log4j.Logger;
6   
7   import java.net.URLEncoder;
8   import java.net.URLDecoder;
9   import java.io.UnsupportedEncodingException;
10  
11  /**
12   * Encrypts the username and password using password-based-encryption (MD5 and DES).
13   *
14   * @deprecated replaced by the {@link com.atlassian.seraph.service.rememberme.RememberMeService} code
15   */
16  public class EncryptedCookieEncoder implements CookieEncoder
17  {
18      private static final Logger LOG = Logger.getLogger(EncryptedCookieEncoder.class);
19  
20      private static final String URL_ENCODING = "UTF-8";
21  
22      private static final String SEPARATOR = "^^^";
23  
24      private final EncryptionUtils encryptionUtils;
25  
26      public EncryptedCookieEncoder()
27      {
28          this(SecurityConfigFactory.getInstance().getCookieEncoding());
29      }
30  
31      protected EncryptedCookieEncoder(final String password)
32      {
33          if ((password == null) || (password.length() == 0))
34          {
35              throw new IllegalArgumentException("The password must be specified");
36          }
37          encryptionUtils = new EncryptionUtils();
38          encryptionUtils.setPassword(password);
39      }
40  
41      public String encodePasswordCookie(final String username, final String password, final String encoding)
42      {
43          final StringBuffer sb = new StringBuffer();
44          sb.append(username).append(EncryptedCookieEncoder.SEPARATOR).append(password);
45          return escapeInvalidCookieCharacters(encryptionUtils.encrypt(sb.toString()));
46      }
47  
48      public String[] decodePasswordCookie(final String cookieVal, final String encoding)
49      {
50          final String[] result = new String[2];
51          try
52          {
53              final String text = encryptionUtils.decrypt(unescapeInvalidCookieCharacters(cookieVal));
54              final int pos = text.indexOf(EncryptedCookieEncoder.SEPARATOR);
55              if (pos < 0)
56              {
57                  EncryptedCookieEncoder.LOG.info("Successfully decrypted password cookie, but decrypted value '" + text + "' is invalid because separator ('" + EncryptedCookieEncoder.SEPARATOR + "') was not found. Returning null.");
58                  return null;
59              }
60              result[0] = text.substring(0, pos);
61              result[1] = text.substring(pos + EncryptedCookieEncoder.SEPARATOR.length());
62          }
63          catch (final RuntimeException ex)
64          {
65              EncryptedCookieEncoder.LOG.info("Password cookie could not be decrypted, trying old insecure method of decoding it");
66              return new InsecureCookieEncoder().decodePasswordCookie(cookieVal, encoding);
67          }
68          return result;
69      }
70  
71      /**
72       * Escape invalid cookie characters, see SER-117
73       * 
74       * @param s
75       *            the String to escape characters for.
76       * @return the encoded string.
77       * @see #unescapeInvalidCookieCharacters(String)
78       */
79      private static String escapeInvalidCookieCharacters(final String s)
80      {
81          try
82          {
83              return URLEncoder.encode(s, EncryptedCookieEncoder.URL_ENCODING);
84          }
85          catch (final UnsupportedEncodingException e)
86          {
87              throw new AssertionError(e);
88          }
89      }
90  
91      /**
92       * Un-escape invalid cookie characters, see SER-117
93       * 
94       * @param s
95       *            the String to escape characters for.
96       * @return the encoded string.
97       * @see #escapeInvalidCookieCharacters(String)
98       */
99      private static String unescapeInvalidCookieCharacters(final String s)
100     {
101         try
102         {
103             return URLDecoder.decode(s, EncryptedCookieEncoder.URL_ENCODING);
104         }
105         catch (final UnsupportedEncodingException e)
106         {
107             EncryptedCookieEncoder.LOG.fatal("UTF-8 encoding unsupported !!?!! How is that possible?", e);
108             throw new RuntimeException(e);
109         }
110     }
111 }