blob: 10ad907c3ac43cd16499eb4977b8011c32464034 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.cxf.fediz.core.util;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collections;
import java.util.Properties;
import org.apache.cxf.fediz.core.exception.ProcessingException;
import org.apache.wss4j.common.crypto.CertificateStore;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoFactory;
import org.apache.wss4j.common.crypto.CryptoType;
import org.apache.wss4j.common.crypto.Merlin;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class CertsUtils {
private static final Logger LOG = LoggerFactory.getLogger(CertsUtils.class);
private CertsUtils() {
super();
}
/**
* Load an X.509 Certificate from a certificate file
*/
public static X509Certificate getX509CertificateFromFile(String filename) throws CertificateException {
return getX509CertificateFromFile(filename,
Thread.currentThread().getContextClassLoader());
}
/**
* Load an X.509 Certificate from a certificate file
*/
public static X509Certificate getX509CertificateFromFile(String filename, ClassLoader classLoader)
throws CertificateException {
if (filename == null) {
return null;
}
ClassLoader cl = classLoader;
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
}
try (InputStream is = Merlin.loadInputStream(cl, filename);
BufferedInputStream bis = new BufferedInputStream(is)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
if (bis.available() > 0) {
Certificate cert = cf.generateCertificate(bis);
if (!(cert instanceof X509Certificate)) {
LOG.error("Certificate " + filename + " is not of type X509Certificate");
throw new RuntimeException("Certificate "
+ filename + " is not of type X509Certificate");
}
if (bis.available() > 0) {
LOG.warn("There are more certificates configured in " + filename + ". Only first is parsed");
}
return (X509Certificate)cert;
} else {
LOG.error("No bytes can be read in certificate file " + filename);
throw new RuntimeException("No bytes can be read in certificate file " + filename);
}
} catch (WSSecurityException ex) {
LOG.error("Failed to read certificate file " + filename, ex);
throw new RuntimeException("Failed to read certificate file " + filename, ex);
} catch (IOException ex) {
LOG.error("Failed to read keystore", ex);
throw new RuntimeException("Failed to read keystore");
}
}
/**
* Load an X.509 Certificate from a WSS4J Crypto instance using a keystore alias
*/
public static X509Certificate getX509CertificateFromCrypto(Crypto crypto, String keyAlias)
throws WSSecurityException {
if (keyAlias == null || "".equals(keyAlias)) {
keyAlias = crypto.getDefaultX509Identifier();
}
if (keyAlias == null) {
throw new RuntimeException("No keystore alias was specified to sign the metadata");
}
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
cryptoType.setAlias(keyAlias);
X509Certificate[] issuerCerts = crypto.getX509Certificates(cryptoType);
if (issuerCerts == null || issuerCerts.length == 0) {
throw new RuntimeException(
"No issuer certs were found to sign the metadata using issuer name: "
+ keyAlias);
}
return issuerCerts[0];
}
/**
* Parse a String parameter into an X.509 Certificate. The parameter can be either the encoded cert, or else
* a filename containing the certificate.
*/
public static X509Certificate parseX509Certificate(String certificate)
throws CertificateException, WSSecurityException, ProcessingException, IOException {
if (certificate == null) {
return null;
}
boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
if (isCertificateLocation) {
try {
return CertsUtils.getX509CertificateFromFile(certificate);
} catch (CertificateException ex) {
// Maybe it's a WSS4J properties file...
Crypto crypto = CertsUtils.getCryptoFromFile(certificate);
if (crypto != null) {
return CertsUtils.getX509CertificateFromCrypto(crypto, null);
}
}
}
// Here the certificate is encoded in the configuration file
try {
return CertsUtils.parseCertificate(certificate);
} catch (Exception ex) {
LOG.error("Failed to parse trusted certificate", ex);
throw new ProcessingException("Failed to parse trusted certificate");
}
}
/**
* Get a Crypto instance from a file
*/
public static Crypto getCryptoFromFile(String filename) {
Properties prop = new Properties();
try (InputStream is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), filename)) {
prop.load(is);
return CryptoFactory.getInstance(prop);
} catch (WSSecurityException ex) {
LOG.error("Failed to load keystore " + prop.toString(), ex);
throw new RuntimeException("Failed to load keystore " + prop.toString());
} catch (IOException ex) {
LOG.error("Failed to read signing metadata key", ex);
throw new RuntimeException("Failed to read signing metadata key");
}
}
/**
* Get a crypto instance using a certificate
*/
public static Crypto getCryptoFromCertificate(String certificate) throws ProcessingException {
if (certificate == null) {
return null;
}
boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
if (isCertificateLocation) {
try {
X509Certificate cert = CertsUtils.getX509CertificateFromFile(certificate);
if (cert == null) {
return null;
}
return new CertificateStore(new X509Certificate[]{cert});
} catch (CertificateException ex) {
// Maybe it's a WSS4J properties file...
return CertsUtils.getCryptoFromFile(certificate);
}
}
// Here the certificate is encoded in the configuration file
X509Certificate cert;
try {
cert = CertsUtils.parseCertificate(certificate);
} catch (Exception ex) {
LOG.error("Failed to parse trusted certificate", ex);
throw new ProcessingException("Failed to parse trusted certificate");
}
return new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
}
private static X509Certificate parseCertificate(String certificate)
throws CertificateException, IOException {
//before decoding we need to get rid off the prefix and suffix
byte[] decoded = Base64.getDecoder().decode(certificate.replaceAll("-----BEGIN CERTIFICATE-----", "").
replaceAll("-----END CERTIFICATE-----", ""));
try (InputStream is = new ByteArrayInputStream(decoded)) {
return (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(is);
}
}
}