/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.model.impl.app; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.app.DBACertificateStorage; import org.jkiss.utils.Base64; import java.io.*; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.List; /** * DefaultCertificateStorage */ public class DefaultCertificateStorage implements DBACertificateStorage { private static final Log log = Log.getLog(DefaultCertificateStorage.class); private static final char[] DEFAULT_PASSWORD = "".toCharArray(); public static final String JKS_EXTENSION = ".jks"; private final File localPath; public DefaultCertificateStorage(File localPath) { this.localPath = localPath; if (localPath.exists()) { // Cleanup old keystores final File[] ksFiles = localPath.listFiles(); if (ksFiles != null) { for (File ksFile : ksFiles) { if (!ksFile.delete()) { log.warn("Can't delete old keystore '" + ksFile.getAbsolutePath() + "'"); } } } } else if (!localPath.mkdirs()) { log.error("Can't create directory for security manager: " + localPath.getAbsolutePath()); } } public KeyStore getKeyStore(DBPDataSourceContainer container, String certType) throws DBException { try { File ksFile = getKeyStorePath(container, certType); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); if (ksFile.exists()) { try (InputStream is = new FileInputStream(ksFile)) { ks.load(is, DEFAULT_PASSWORD); } } else { ks.load(null, DEFAULT_PASSWORD); saveKeyStore(container, certType, ks); } return ks; } catch (Exception e) { throw new DBException("Error opening keystore", e); } } private void saveKeyStore(DBPDataSourceContainer container, String certType, KeyStore keyStore) throws Exception { final File ksFile = getKeyStorePath(container, certType); try (OutputStream os = new FileOutputStream(ksFile)) { keyStore.store(os, DEFAULT_PASSWORD); } } public static byte[] readEncryptedString(InputStream stream) throws IOException { try (Reader reader = new InputStreamReader(stream)) { return readEncryptedString(reader); } } public static byte[] readEncryptedString(Reader reader) throws IOException { StringBuilder result = new StringBuilder(4000); try (BufferedReader br = new BufferedReader(reader)) { for (; ; ) { final String line = br.readLine(); if (line == null || line.isEmpty()) break; if (line.startsWith("-") || line.startsWith("#")) continue; result.append(line); } } return Base64.decode(result.toString()); } @Override public void addCertificate(DBPDataSourceContainer dataSource, String certType, byte[] caCertData, byte[] clientCertData, byte[] keyData) throws DBException { final KeyStore keyStore = getKeyStore(dataSource, certType); try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); List<Certificate> certChain = new ArrayList<>(); if (caCertData != null) { Certificate caCert = cf.generateCertificate(new ByteArrayInputStream(caCertData)); keyStore.setCertificateEntry("ca-cert", caCert); //certChain.add(caCert); } if (clientCertData != null) { Certificate clientCert = cf.generateCertificate(new ByteArrayInputStream(clientCertData)); keyStore.setCertificateEntry("client-cert", clientCert); certChain.add(clientCert); } if (keyData != null) { PrivateKey privateKey = loadPrivateKeyFromPEM(keyData); keyStore.setKeyEntry("key-cert", privateKey, DEFAULT_PASSWORD, certChain.toArray(new Certificate[certChain.size()])); } saveKeyStore(dataSource, certType, keyStore); } catch (Throwable e) { throw new DBException("Error adding certificate to keystore", e); } } @Override public void deleteCertificate(DBPDataSourceContainer dataSource, String certType) throws DBException { final KeyStore keyStore = getKeyStore(dataSource, certType); try { keyStore.deleteEntry("ca-cert"); keyStore.deleteEntry("client-cert"); keyStore.deleteEntry("key-cert"); saveKeyStore(dataSource, certType, keyStore); } catch (Exception e) { throw new DBException("Error deleting certificate from keystore", e); } } @Override public File getKeyStorePath(DBPDataSourceContainer dataSource, String certType) { return new File(localPath, dataSource.getId() + "-" + certType + JKS_EXTENSION); } /** * That's tricky. * Algorithm got from http://stackoverflow.com/questions/7216969/getting-rsa-private-key-from-pem-base64-encoded-private-key-file * Different SSL providers use different pk format. Google cloud uses PKCS1. See #740 */ public static PrivateKey loadPrivateKeyFromPEM(byte[] keyData) throws GeneralSecurityException, IOException { // PKCS#8 format final String PEM_PRIVATE_START = "-----BEGIN PRIVATE KEY-----"; final String PEM_PRIVATE_END = "-----END PRIVATE KEY-----"; // PKCS#1 format final String PEM_RSA_PRIVATE_START = "-----BEGIN RSA PRIVATE KEY-----"; final String PEM_RSA_PRIVATE_END = "-----END RSA PRIVATE KEY-----"; String privateKeyPem = new String(keyData); if (privateKeyPem.contains(PEM_PRIVATE_START)) { // PKCS#8 format privateKeyPem = privateKeyPem.replace(PEM_PRIVATE_START, "").replace(PEM_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); byte[] pkcs8EncodedKey = Base64.decode(privateKeyPem); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); } else if (privateKeyPem.contains(PEM_RSA_PRIVATE_START)) { // PKCS#1 format privateKeyPem = privateKeyPem.replace(PEM_RSA_PRIVATE_START, "").replace(PEM_RSA_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); return PKCS1Util.loadPrivateKeyFromPKCS1(privateKeyPem); } else { throw new GeneralSecurityException("Not supported format of a private key"); } } }