/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you 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.openengsb.core.services.internal.security;
import java.io.File;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.io.FileUtils;
import org.openengsb.core.api.security.PrivateKeySource;
import org.openengsb.core.api.security.PublicKeySource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
/**
* public and private keys form binary files in a specific directory. The files are expected to have the names:
* {public,private}.key.data
*
* If the configured path is not absolute it is assumed to be a subdirectory of ${karaf.data}
*
* Keys are only read once and then kept in memory, so it does not act like a deployer.
*
*/
public class FileKeySource implements PrivateKeySource, PublicKeySource {
private static final String DEFAULT_KEY_DIR = "etc/keys";
private static final Logger LOGGER = LoggerFactory.getLogger(FileKeySource.class);
private static final String DEFAULT_PRIVATE_KEY_FILENAME = "private.key.data";
private static final String DEFAULT_PUBLIC_KEY_FILENAME = "public.key.data";
private static final String DEFAULT_ALGORITHM = "RSA";
private static final int DEFAULT_KEY_SIZE = 2048;
private String algorithm;
private PrivateKey privateKey;
private PublicKey publicKey;
private String keyDirectory;
public FileKeySource() {
}
public FileKeySource(String keyDirectory, String algorithm) {
setAlgorithm(algorithm);
setKeyDirectory(keyDirectory);
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
private KeyFactory getKeyFactory() {
try {
return KeyFactory.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
public void setKeyDirectory(String keyDirectory) {
this.keyDirectory = keyDirectory;
}
private File getKeyDirectoryFile() {
if (keyDirectory == null) {
LOGGER.info("no key-directory defined, defaulting to {}", DEFAULT_KEY_DIR);
keyDirectory = DEFAULT_KEY_DIR;
}
LOGGER.debug("using {} as keyDirectory", keyDirectory);
File keyDirectoryFile = new File(keyDirectory);
if (keyDirectoryFile.isAbsolute()) {
return keyDirectoryFile;
}
LOGGER.info("understanding {} to be a subdirectory of karaf.data", keyDirectory);
return new File(System.getProperty("karaf.home"), keyDirectory);
}
public void init() {
LOGGER.trace("initialize FileKeySource");
File keyDirectoryFile = getKeyDirectoryFile();
makeSureDirectoryExists(keyDirectoryFile);
generateKeysIfRequired(keyDirectoryFile);
privateKey = readPrivateKeyFromFile(keyDirectoryFile);
publicKey = readPublicKeyFromFile(keyDirectoryFile);
}
private void makeSureDirectoryExists(File keyDirectoryFile) {
if (keyDirectoryFile.exists()) {
Preconditions.checkState(keyDirectoryFile.isDirectory(), "%s is not a directory", keyDirectoryFile);
} else {
LOGGER.info("creating keydir: {}", keyDirectoryFile.getAbsolutePath());
keyDirectoryFile.mkdirs();
}
}
private void generateKeysIfRequired(File keyDirectoryFile) {
File privateKeyFile = new File(keyDirectoryFile, DEFAULT_PRIVATE_KEY_FILENAME);
File publicKeyFile = new File(keyDirectoryFile, DEFAULT_PUBLIC_KEY_FILENAME);
if (privateKeyFile.exists() && publicKeyFile.exists()) {
LOGGER.info("skipping key-generation, because there already are some");
return;
}
KeyPairGenerator generator;
try {
LOGGER.info("generating new keypair");
generator = KeyPairGenerator.getInstance(DEFAULT_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("failed to generate keypair", e);
}
generator.initialize(DEFAULT_KEY_SIZE);
KeyPair generatedKeyPair = generator.generateKeyPair();
try {
LOGGER.trace("saving new keypair to files");
FileUtils.writeByteArrayToFile(privateKeyFile, generatedKeyPair.getPrivate().getEncoded());
FileUtils.writeByteArrayToFile(publicKeyFile, generatedKeyPair.getPublic().getEncoded());
} catch (IOException e) {
throw new IllegalStateException("failed to write keys to key-directory", e);
}
}
protected PrivateKey readPrivateKeyFromFile(File keyDirectory) {
byte[] keyData;
try {
File file = new File(keyDirectory, DEFAULT_PRIVATE_KEY_FILENAME);
LOGGER.trace("reading private key form file: {}", file.getAbsolutePath());
keyData = FileUtils.readFileToByteArray(file);
} catch (IOException e) {
throw new IllegalStateException(e);
}
KeySpec keySpec = new PKCS8EncodedKeySpec(keyData);
try {
return getKeyFactory().generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException(e);
}
}
protected PublicKey readPublicKeyFromFile(File keyDirectory) {
byte[] keyData;
try {
File file = new File(keyDirectory, DEFAULT_PUBLIC_KEY_FILENAME);
LOGGER.trace("reading private key form file: {}", file.getAbsolutePath());
keyData = FileUtils.readFileToByteArray(file);
} catch (IOException e) {
throw new IllegalStateException(e);
}
KeySpec keySpec = new X509EncodedKeySpec(keyData);
try {
return getKeyFactory().generatePublic(keySpec);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException(e);
}
}
@Override
public PrivateKey getPrivateKey() {
return privateKey;
}
@Override
public PublicKey getPublicKey() {
return publicKey;
}
}