package io.cattle.platform.host.service;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.core.dao.DataDao;
import io.cattle.platform.ssh.common.SshKeyGen;
import io.cattle.platform.token.CertSet;
import io.cattle.platform.token.impl.RSAKeyProvider;
import io.cattle.platform.token.impl.RSAPrivateKeyHolder;
import io.cattle.platform.util.exception.ExceptionUtils;
import io.cattle.platform.util.type.InitializationTask;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import com.netflix.config.DynamicBooleanProperty;
public class HostApiRSAKeyProvider implements RSAKeyProvider, InitializationTask {
private static final DynamicBooleanProperty GEN_ON_STARTUP = ArchaiusUtil.getBoolean("host.api.keygen.on.startup");
private static final String KEY = "host.api.key";
private static final String CERT = "host.api.key.cert";
private static final String DEFAULT = "default";
DataDao dataDao;
@Override
public RSAPrivateKeyHolder getPrivateKey() {
KeyPair kp = getKeyPair();
if (kp == null) {
return null;
}
return new RSAPrivateKeyHolder(DEFAULT, (RSAPrivateKey) kp.getPrivate());
}
@Override
public void start() {
if (GEN_ON_STARTUP.get()) {
getPrivateKey();
getCACertificate();
}
}
protected KeyPair getKeyPair() {
String encoded = dataDao.getOrCreate(KEY, false, new Callable<String>() {
@Override
public String call() throws Exception {
KeyPair kp = SshKeyGen.generateKeyPair();
return SshKeyGen.toPEM(kp);
}
});
try {
return SshKeyGen.readKeyPair(encoded);
} catch (Exception e) {
ExceptionUtils.throwRuntime("Failed to read key pair from PEM", e);
/* Won't hit next line */
return null;
}
}
@Override
public X509Certificate getCACertificate() {
final KeyPair kp = getKeyPair();
String encoded = dataDao.getOrCreate(CERT, false, new Callable<String>() {
@Override
public String call() throws Exception {
X509Certificate cert = SshKeyGen.createRootCACert(kp);
return SshKeyGen.toPEM(cert);
}
});
try {
return SshKeyGen.readCACert(encoded);
} catch (Exception e) {
ExceptionUtils.throwRuntime("Failed to CA cert from PEM", e);
/* Won't hit next line */
return null;
}
}
@Override
public CertSet generateCertificate(String subject, String... sans) throws Exception {
KeyPair caKp = getKeyPair();
X509Certificate caCert = getCACertificate();
KeyPair clientKp = SshKeyGen.generateKeyPair();
X509Certificate clientCert = SshKeyGen.generateClientCert(subject, clientKp.getPublic(), caKp.getPrivate(), caCert, sans);
CertSet result = new CertSet(caCert, clientCert, clientKp.getPrivate());
return result;
}
@Override
public PublicKey getDefaultPublicKey() {
return getPublicKeys().get(DEFAULT);
}
@Override
public Map<String, PublicKey> getPublicKeys() {
Map<String, PublicKey> result = new HashMap<>();
KeyPair defaultKp = getKeyPair();
if (defaultKp != null) {
result.put(DEFAULT, defaultKp.getPublic());
}
return result;
}
public DataDao getDataDao() {
return dataDao;
}
@Inject
public void setDataDao(DataDao dataDao) {
this.dataDao = dataDao;
}
@Override
public byte[] toBytes(Certificate cert) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(baos))) {
writer.writeObject(cert);
}
return baos.toByteArray();
}
}