org.postgresql.ssl.jdbc4.LazyKeyManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of postgresql Show documentation
Show all versions of postgresql Show documentation
PostgreSQL JDBC Driver JDBC4
/*
* Copyright (c) 2004, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.ssl.jdbc4;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
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.Certificate;
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;
/**
* 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 certfile
* @param keyfile key file
* @param cbh callback handler
* @param defaultfile default file
*/
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 {@link #error} and can be raised by this method
*
* @throws PSQLException if any exception is stored in {@link #error} and can be raised
*/
public void throwKeyManagerException() throws PSQLException {
if (error != null) {
throw error;
}
}
@Override
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 (Principal issuer : issuers) {
if (ourissuer.equals(issuer)) {
found = true;
}
}
return (found ? "user" : null);
}
}
}
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return null; // We are not a server
}
@Override
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."),
PSQLState.CONNECTION_FAILURE, ex);
return null;
}
Collection extends Certificate> 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}.", certfile),
PSQLState.CONNECTION_FAILURE, ioex);
}
return null;
} catch (CertificateException gsex) {
error = new PSQLException(GT.tr("Loading the SSL certificate {0} into a KeyManager failed.",
certfile), PSQLState.CONNECTION_FAILURE, gsex);
return null;
}
cert = certs.toArray(new X509Certificate[certs.size()]);
}
return cert;
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
String alias = chooseClientAlias(new String[]{keyType}, issuers, (Socket) null);
return (alias == null ? new String[]{} : new String[]{alias});
}
@Override
public PrivateKey getPrivateKey(String alias) {
RandomAccessFile raf = null;
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...
}
}
try {
raf = new RandomAccessFile(new File(keyfile), "r"); // NOSONAR
} 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) raf.length()];
raf.readFully(keydata);
raf.close();
raf = null;
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."),
PSQLState.CONNECTION_FAILURE, ucex);
} else {
error =
new PSQLException(
GT.tr("Could not read password for SSL key file by callbackhandler {0}.",
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}.", keyfile),
PSQLState.CONNECTION_FAILURE, ikex);
return null;
}
}
}
} catch (IOException ioex) {
if (raf != null) {
try {
raf.close();
} catch (IOException ex) {
}
}
error = new PSQLException(GT.tr("Could not read SSL key file {0}.", keyfile),
PSQLState.CONNECTION_FAILURE, ioex);
} catch (NoSuchAlgorithmException ex) {
error = new PSQLException(GT.tr("Could not find a java cryptographic algorithm: {0}.",
ex.getMessage()), PSQLState.CONNECTION_FAILURE, ex);
return null;
}
return key;
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return new String[]{};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy