File: LazyKeyManager.java

package info (click to toggle)
libpgjava 9.2-1002-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 4,308 kB
  • ctags: 4,503
  • sloc: java: 37,623; xml: 3,376; makefile: 22; sh: 10
file content (247 lines) | stat: -rw-r--r-- 9,291 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package org.postgresql.ssl.jdbc4;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collection;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.X509KeyManager;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.x500.X500Principal;

import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;

/**
 * A Key manager that only loads the keys, if necessary.
 *
 */
public class LazyKeyManager implements X509KeyManager {
  private X509Certificate[] cert = null;
  private PrivateKey key = null;
  private String certfile;
  private String keyfile;
  private CallbackHandler cbh;
  private boolean defaultfile;
  private PSQLException error = null;
  
  /**
   * Constructor. certfile and keyfile can be null, in that case no
   * certificate is presented to the server.
   * @param certfile
   * @param keyfile
   * @param cbh
   * @param defaultfile
   */
  public LazyKeyManager(String certfile, String keyfile, CallbackHandler cbh, boolean defaultfile) {
    this.certfile = certfile;
    this.keyfile = keyfile;
    this.cbh = cbh;
    this.defaultfile = defaultfile;
  }
  
  /**
   * getCertificateChain and getPrivateKey cannot throw exeptions,
   * therefore any exception is stored in this.error and can be raised
   * by this method
   * @throws PSQLException
   */
  public void throwKeyManagerException() throws PSQLException
  {
    if (error!=null) throw error;
  }
  
  public String chooseClientAlias(String[] keyType, Principal[] issuers,
      Socket socket) {
    if (certfile==null)
    {
      return null;
    } else {
      if (issuers==null || issuers.length==0)
      { //Postgres 8.4 and earlier do not send the list of accepted certificate authorities
        //to the client. See BUG #5468. We only hope, that our certificate will be accepted.
        return "user";
      } else { //Sending a wrong certificate makes the connection rejected, even, if clientcert=0 in pg_hba.conf.
        //therefore we only send our certificate, if the issuer is listed in issuers
        X509Certificate[] certchain = getCertificateChain("user");
        if (certchain==null)
        {
          return null;
        } else {
          X500Principal ourissuer = certchain[certchain.length-1].getIssuerX500Principal();
          boolean found = false;
          for (int i=0; i<issuers.length; i++)
          {
            if (ourissuer.equals(issuers[i])) { found = true;}
          }
          return (found ? "user" : null);
        }
      }
    }
  }

  public String chooseServerAlias(String keyType, Principal[] issuers,
      Socket socket) {
    return null; //We are not a server
  }

  public X509Certificate[] getCertificateChain(String alias) {
    if (cert==null && certfile!=null) //If certfile is null, we do not load the certificate
    { //The certificate must be loaded
      CertificateFactory cf;
      try
      {
        cf = CertificateFactory.getInstance("X.509");
      } catch (CertificateException ex)
      { //For some strange reason it throws CertificateException instead of NoSuchAlgorithmException...
        error = new PSQLException(GT.tr("Could not find a java cryptographic algorithm: X.509 CertificateFactory not available.", null), PSQLState.CONNECTION_FAILURE, ex);
        return null;
      }
      Collection certs;
      try
      {
        certs = cf.generateCertificates(new FileInputStream(certfile));
      } catch (FileNotFoundException ioex)
      {
        if (!defaultfile)
        { //It is not an error if there is no file at the default location
          error = new PSQLException(GT.tr("Could not open SSL certificate file {0}.", new Object[]{certfile}), PSQLState.CONNECTION_FAILURE, ioex);
        }
        return null;
      }catch (CertificateException gsex)
      {
        error = new PSQLException(GT.tr("Loading the SSL certificate {0} into a KeyManager failed.", new Object[]{certfile}), PSQLState.CONNECTION_FAILURE, gsex);
        return null;
      } 
      cert = (X509Certificate[]) certs.toArray(new X509Certificate[certs.size()]);
    }    
    return cert;
  }

  public String[] getClientAliases(String keyType, Principal[] issuers) {
    String alias = chooseClientAlias(new String[]{keyType}, issuers, (Socket)null);
    return (alias==null ? new String[]{} : new String[]{alias});
  }

  public PrivateKey getPrivateKey(String alias) {
    try
    {
      if (key==null && keyfile!=null) //If keyfile is null, we do not load the key
      { //The private key must be loaded
        if (cert==null)
        { //We need the certificate for the algorithm
          if(getCertificateChain("user")==null)
            return null; //getCertificateChain failed...
        }
        File keyf = new File(keyfile);
        FileInputStream fl;
        try
        {
          fl = new FileInputStream(keyfile);
        }
        catch (FileNotFoundException ex)
        {
          if (!defaultfile)
          { //It is not an error if there is no file at the default location
            throw ex;
          }
          return null;      
        }
        byte[] keydata = new byte[(int)keyf.length()];
        fl.read ( keydata, 0, (int)keyf.length() );
        fl.close();
        KeyFactory kf = KeyFactory.getInstance(cert[0].getPublicKey().getAlgorithm());
        try {
          KeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec (keydata);
          key = kf.generatePrivate (pkcs8KeySpec);
        }
        catch (InvalidKeySpecException ex) //The key might be password protected
        {
          EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(keydata);
          Cipher cipher;
          try
          {
            cipher = Cipher.getInstance(ePKInfo.getAlgName());
          } catch (NoSuchPaddingException npex)
          { //Why is it not a subclass of NoSuchAlgorithmException?
            throw new NoSuchAlgorithmException(npex.getMessage(),npex);
          }
          //We call back for the password
          PasswordCallback pwdcb = new PasswordCallback(GT.tr("Enter SSL password: "), false);
          try
          {
            cbh.handle(new Callback[]{pwdcb});
          } catch (UnsupportedCallbackException ucex)
          {
            if ((cbh instanceof LibPQFactory.ConsoleCallbackHandler) && ("Console is not available".equals(ucex.getMessage())))
            {
              error = new PSQLException(GT.tr("Could not read password for SSL key file, console is not available.", null), PSQLState.CONNECTION_FAILURE, ucex);
            } else {
              error = new PSQLException(GT.tr("Could not read password for SSL key file by callbackhandler {0}.", new Object[]{cbh.getClass().getName()}), PSQLState.CONNECTION_FAILURE, ucex); 
            }
            return null;
          }
          try
          {
            PBEKeySpec pbeKeySpec = new PBEKeySpec(pwdcb.getPassword());
            // Now create the Key from the PBEKeySpec
            SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo.getAlgName());
            Key pbeKey = skFac.generateSecret(pbeKeySpec);
            // Extract the iteration count and the salt
            AlgorithmParameters algParams = ePKInfo.getAlgParameters();
            cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
            // Decrypt the encryped private key into a PKCS8EncodedKeySpec
            KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
            key = kf.generatePrivate (pkcs8KeySpec);
          }
          catch (GeneralSecurityException ikex)
          {
            error = new PSQLException(GT.tr("Could not decrypt SSL key file {0}.", new Object[]{keyfile}), PSQLState.CONNECTION_FAILURE, ikex);
            return null;      
          }         
        }
      }
    }
    catch (IOException ioex)
    {
      error = new PSQLException(GT.tr("Could not read SSL key file {0}.", new Object[]{keyfile}), PSQLState.CONNECTION_FAILURE, ioex);
    }
    catch(NoSuchAlgorithmException ex)
    {
      error = new PSQLException(GT.tr("Could not find a java cryptographic algorithm: {0}.", new Object[]{ex.getMessage()}), PSQLState.CONNECTION_FAILURE, ex);
      return null;      
    }

    return key;
  }

  public String[] getServerAliases(String keyType, Principal[] issuers) {
    return new String[]{};
  }
  

}