View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.util;
21  
22  import davmail.Settings;
23  
24  import javax.crypto.BadPaddingException;
25  import javax.crypto.Cipher;
26  import javax.crypto.SecretKey;
27  import javax.crypto.SecretKeyFactory;
28  import javax.crypto.spec.IvParameterSpec;
29  import javax.crypto.spec.PBEKeySpec;
30  import javax.crypto.spec.PBEParameterSpec;
31  import java.io.IOException;
32  import java.nio.charset.StandardCharsets;
33  import java.security.NoSuchAlgorithmException;
34  import java.security.spec.InvalidKeySpecException;
35  
36  /**
37   * Encrypt string using user password.
38   * Simple implementation based on AES
39   */
40  public class StringEncryptor {
41      static final String ALGO = "PBEWithHmacSHA256AndAES_128";
42      static final String DEFAULT_FINGERPRINT = "davmailgateway!&";
43  
44      private final String password;
45  
46      public StringEncryptor(String password) {
47          this.password = password;
48      }
49  
50      public String encryptString(String value) throws IOException {
51          if (value == null) {
52              return null;
53          }
54          if (value.isEmpty()) {
55              return "";
56          }
57          try {
58              byte[] plaintext = value.getBytes(StandardCharsets.UTF_8);
59  
60              // Encrypt
61              Cipher enc = Cipher.getInstance(ALGO);
62              enc.init(Cipher.ENCRYPT_MODE, getSecretKey(), getPBEParameterSpec());
63              byte[] encrypted = enc.doFinal(plaintext);
64              return "{AES}" + IOUtil.encodeBase64AsString(encrypted);
65  
66          } catch (Exception e) {
67              throw new IOException(e);
68          }
69      }
70  
71      public String decryptString(String value) throws IOException {
72          if (value != null && value.startsWith("{AES}")) {
73              try {
74                  byte[] encrypted = IOUtil.decodeBase64(value.substring(5));
75  
76                  Cipher dec = Cipher.getInstance(ALGO);
77                  dec.init(Cipher.DECRYPT_MODE, getSecretKey(), getPBEParameterSpec());
78                  byte[] decrypted = dec.doFinal(encrypted);
79                  return new String(decrypted, StandardCharsets.UTF_8);
80  
81              } catch (BadPaddingException e) {
82                  throw new IOException("Unable to decrypt token, invalid password");
83              } catch (Exception e) {
84                  throw new IOException(e);
85              }
86          } else {
87              return value;
88          }
89      }
90  
91      private SecretKey getSecretKey() throws InvalidKeySpecException, NoSuchAlgorithmException {
92          PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
93  
94          SecretKeyFactory kf = SecretKeyFactory.getInstance(ALGO);
95          return kf.generateSecret(keySpec);
96      }
97  
98      private PBEParameterSpec getPBEParameterSpec() {
99          byte[] bytes = Settings.getProperty("davmail.oauth.fingerprint", DEFAULT_FINGERPRINT).getBytes(StandardCharsets.UTF_8);
100         return new PBEParameterSpec(bytes, 10000, new IvParameterSpec(bytes));
101     }
102 }