MerlinProvider.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.security.keystore;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Properties;
import org.apache.wss4j.common.crypto.PasswordEncryptor;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.ext.WSSecurityException.ErrorCode;
import org.openspcoop2.core.config.MessageSecurity;
import org.openspcoop2.core.config.MessageSecurityFlow;
import org.openspcoop2.core.config.MessageSecurityFlowParameter;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.security.keystore.cache.GestoreKeystoreCache;
import org.openspcoop2.utils.certificate.KeystoreType;
import org.openspcoop2.utils.certificate.byok.BYOKProvider;
import org.openspcoop2.utils.certificate.byok.BYOKRequestParams;
/**
* Implementazione che estente l'implementazione di default e permette di caricare keystore utilizzando la cache o il binario passato direttamente.
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class MerlinProvider extends org.apache.wss4j.common.crypto.Merlin {
private static boolean useBouncyCastleProvider = false;
public static boolean isUseBouncyCastleProvider() {
return useBouncyCastleProvider;
}
public static void setUseBouncyCastleProvider(boolean useBouncyCastleProvider) {
MerlinProvider.useBouncyCastleProvider = useBouncyCastleProvider;
}
public MerlinProvider() {
super();
}
public MerlinProvider(Properties properties, ClassLoader loader, PasswordEncryptor passwordEncryptor)
throws WSSecurityException, IOException {
super(properties, loader, passwordEncryptor);
}
private org.openspcoop2.utils.certificate.KeyStore op2KeyStore;
private org.openspcoop2.utils.certificate.KeyStore op2TrustStore;
public org.openspcoop2.utils.certificate.KeyStore getOp2KeyStore() {
return this.op2KeyStore;
}
public org.openspcoop2.utils.certificate.KeyStore getOp2TrustStore() {
return this.op2TrustStore;
}
private Boolean useBouncyCastleProviderDirective = null;
public static String readPrefix(Properties properties) {
for (Object key : properties.keySet()) {
if (key instanceof String) {
String propKey = (String)key;
if (propKey.startsWith(PREFIX)) {
return PREFIX;
} else if (propKey.startsWith(OLD_PREFIX)) {
return OLD_PREFIX;
}
}
}
return PREFIX;
}
private static final String TRUST_STORE_REF = "truststore";
private static final String KEY_STORE_REF = "keystore";
private String getError(String tipoStore, String location) {
if(location!=null) {
return "Accesso al "+tipoStore+" '"+location+"' non riuscito";
}
else {
return "Accesso al "+tipoStore+" non riuscito";
}
}
public static final String SUFFIX_BYOK = ".byok";
public static final String X509_CRL_FILE_VALIDATE_ONLY_END_ENTITY = X509_CRL_FILE + ".validateOnlyEndEntity";
private static final String TRUE = "true";
private static final String FALSE = "false";
@Override
public void loadProperties(Properties properties, ClassLoader loader, PasswordEncryptor passwordEncryptor)
throws WSSecurityException, IOException {
if (properties == null) {
return;
}
this.properties = properties;
this.passwordEncryptor = passwordEncryptor;
String prefix = readPrefix(properties);
//
// Load the RequestInfo
//
RequestInfo requestInfo = null;
Object requestInfoObject = properties.get(KeystoreConstants.PROPERTY_REQUEST_INFO);
if(requestInfoObject instanceof RequestInfo) {
requestInfo = (RequestInfo) requestInfoObject;
}
//
// Load the KeyStore
//
String keyStoreLocation = properties.getProperty(prefix + KEYSTORE_FILE);
if (keyStoreLocation == null) {
keyStoreLocation = properties.getProperty(prefix + OLD_KEYSTORE_FILE);
}
Object keyStoreArchiveObject = properties.get(prefix + KeystoreConstants.KEYSTORE);
byte [] keyStoreArchive = null;
if(keyStoreArchiveObject instanceof byte[]) {
keyStoreArchive = (byte[]) keyStoreArchiveObject;
}
String keyStorePassword = properties.getProperty(prefix + KEYSTORE_PASSWORD, "security");
if (keyStorePassword != null) {
keyStorePassword = keyStorePassword.trim();
keyStorePassword = decryptPassword(keyStorePassword, passwordEncryptor);
}
String keyStoreType = properties.getProperty(prefix + KEYSTORE_TYPE, KeyStore.getDefaultType());
if (keyStoreType != null) {
keyStoreType = keyStoreType.trim();
}
if (keyStoreLocation != null) {
String keyStoreByokPolicy = properties.getProperty(prefix + KEYSTORE_FILE+SUFFIX_BYOK);
BYOKRequestParams byokParams = null;
if (keyStoreByokPolicy == null) {
keyStoreByokPolicy = properties.getProperty(prefix + OLD_KEYSTORE_FILE+SUFFIX_BYOK);
}
if (keyStoreByokPolicy != null) {
keyStoreByokPolicy = keyStoreByokPolicy.trim();
if(BYOKProvider.isPolicyDefined(keyStoreByokPolicy)){
try {
byokParams = BYOKProvider.getBYOKRequestParamsByUnwrapBYOKPolicy(keyStoreByokPolicy,
requestInfo!=null && requestInfo.getDynamicMap()!=null ? requestInfo.getDynamicMap() : new HashMap<>() );
}catch(Exception e) {
throw new IOException(e.getMessage(),e);
}
}
}
// rimuovo la proprietà per non farla trovare quando chiamo il super.loadProperties
this.properties.remove(prefix + KEYSTORE_FILE);
this.properties.remove(prefix + OLD_KEYSTORE_FILE);
// il set 'privatePasswordSet' e' stato riportato poichè eliminando sopra le due proprietà, questo codice non verra utilizzato nel super.loadProperties
String privatePasswd = properties.getProperty(prefix + KEYSTORE_PRIVATE_PASSWORD);
if (privatePasswd != null) {
this.privatePasswordSet = true;
}
keyStoreLocation = keyStoreLocation.trim();
try {
MerlinKeystore merlinKs = GestoreKeystoreCache.getMerlinKeystore(requestInfo, keyStoreLocation, keyStoreType,
keyStorePassword, byokParams);
if(merlinKs==null) {
throw new IOException(getError(KEY_STORE_REF,keyStoreLocation));
}
if(merlinKs.getKeyStore()==null) {
throw new IOException(getError(KEY_STORE_REF,keyStoreLocation));
}
this.op2KeyStore = merlinKs.getKeyStore();
this.keystore = this.op2KeyStore.getKeystore();
}catch(Exception e) {
throw new IOException("[Keystore-File] '"+keyStoreLocation+"' "+e.getMessage(),e);
}
}
else if (keyStoreArchive != null) {
try {
MerlinKeystore merlinKs = GestoreKeystoreCache.getMerlinKeystore(requestInfo, keyStoreArchive, keyStoreType,
keyStorePassword);
if(merlinKs==null) {
throw new IOException(getError(KEY_STORE_REF,null));
}
if(merlinKs.getKeyStore()==null) {
throw new IOException(getError(KEY_STORE_REF,null));
}
this.op2KeyStore = merlinKs.getKeyStore();
this.keystore = this.op2KeyStore.getKeystore();
}catch(Exception e) {
throw new IOException("[Keystore-Archive] "+e.getMessage(),e);
}
}
//
// Load the TrustStore
//
String trustStoreLocation = properties.getProperty(prefix + TRUSTSTORE_FILE);
Object trustStoreArchiveObject = properties.get(prefix + KeystoreConstants.TRUSTSTORE);
byte [] trustStoreArchive = null;
if(trustStoreArchiveObject instanceof byte[]) {
trustStoreArchive = (byte[]) trustStoreArchiveObject;
}
String trustStorePassword = properties.getProperty(prefix + TRUSTSTORE_PASSWORD, "security");
if (trustStorePassword != null) {
trustStorePassword = trustStorePassword.trim();
trustStorePassword = decryptPassword(trustStorePassword, passwordEncryptor);
}
String trustStoreType = properties.getProperty(prefix + TRUSTSTORE_TYPE, KeyStore.getDefaultType());
if (trustStoreType != null) {
trustStoreType = trustStoreType.trim();
}
if (trustStoreLocation != null) {
// rimuovo la proprietà per non farla trovare quando chiamo il super.loadProperties
this.properties.remove(prefix + TRUSTSTORE_FILE);
// il set 'loadCACerts' e' stato riportato poichè eliminando sopra le due proprietà, questo codice non verra utilizzato nel super.loadProperties
this.loadCACerts = false;
trustStoreLocation = trustStoreLocation.trim();
try {
MerlinTruststore merlinTs = GestoreKeystoreCache.getMerlinTruststore(requestInfo, trustStoreLocation, trustStoreType,
trustStorePassword);
if(merlinTs==null) {
throw new IOException(getError(TRUST_STORE_REF,trustStoreLocation));
}
if(merlinTs.getTrustStore()==null) {
throw new IOException(getError(TRUST_STORE_REF,trustStoreLocation));
}
this.op2TrustStore = merlinTs.getTrustStore();
this.truststore = this.op2TrustStore.getKeystore();
}catch(Exception e) {
throw new IOException("[Truststore-File] '"+trustStoreLocation+"' "+e.getMessage(),e);
}
}
else if (trustStoreArchive != null) {
try {
MerlinTruststore merlinTs = GestoreKeystoreCache.getMerlinTruststore(requestInfo, trustStoreArchive, trustStoreType,
trustStorePassword);
if(merlinTs==null) {
throw new IOException(getError(TRUST_STORE_REF,null));
}
if(merlinTs.getTrustStore()==null) {
throw new IOException(getError(TRUST_STORE_REF,null));
}
this.op2TrustStore = merlinTs.getTrustStore();
this.truststore = this.op2TrustStore.getKeystore();
}catch(Exception e) {
throw new IOException("[Truststore-Archive] "+e.getMessage(),e);
}
}
if(this.truststore!=null) {
// Devo evitare che venga eseguito il loadCerts quando invoco super.loadProperties
properties.remove(prefix + LOAD_CA_CERTS);
properties.setProperty(prefix + LOAD_CA_CERTS, FALSE);
}
//
// Load the CRL file(s)
//
String crlLocations = properties.getProperty(prefix + X509_CRL_FILE);
if (crlLocations != null) {
// rimuovo la proprietà per non farla trovare quando chiamo il super.loadProperties
this.properties.remove(prefix + X509_CRL_FILE);
CRLCertstore crlCertstore = null;
try {
crlCertstore = GestoreKeystoreCache.getCRLCertstore(requestInfo, crlLocations);
if(crlCertstore==null) {
throw new IOException("Accesso alle crl '"+crlLocations+"' non riuscito");
}
this.crlCertStore = crlCertstore.getCertStore();
}catch(Exception e) {
throw new IOException("[CRLCertstore] "+e.getMessage(),e);
}
String useBouncyCastleProviderProperty = properties.getProperty(prefix + "useBouncyCastleProvider");
if (useBouncyCastleProviderProperty != null) {
if(TRUE.equalsIgnoreCase(useBouncyCastleProviderProperty.trim())) {
this.useBouncyCastleProviderDirective = true;
}
else if(FALSE.equalsIgnoreCase(useBouncyCastleProviderProperty.trim())) {
this.useBouncyCastleProviderDirective = false;
}
}
String validateOnlyEndEntity = properties.getProperty(prefix + X509_CRL_FILE_VALIDATE_ONLY_END_ENTITY);
if (validateOnlyEndEntity != null) {
if(TRUE.equalsIgnoreCase(validateOnlyEndEntity.trim())) {
this.setValidateOnlyEndEntity(true);
}
else if(FALSE.equalsIgnoreCase(validateOnlyEndEntity.trim())) {
this.setValidateOnlyEndEntity(false);
}
}
else if(crlCertstore!=null && crlCertstore.getWrappedCRLCertStore()!=null && crlCertstore.getWrappedCRLCertStore().countCrls()==1) {
// per default si assume che venga fornito solo il file CRL del certificato finale
this.setValidateOnlyEndEntity(true);
}
}
//
// Super
//
super.loadProperties(properties, loader, passwordEncryptor);
}
/**@Override
public PrivateKey getPrivateKey(PublicKey publicKey, CallbackHandler callbackHandler) throws WSSecurityException {
System.out.println("@@@ getPrivateKey PUBLIC KEY, CALLBACK");
return super.getPrivateKey(publicKey, callbackHandler);
}*/
@Override
public PrivateKey getPrivateKey(String alias, String password) throws WSSecurityException {
/**System.out.println("@@@ getPrivateKey arg0["+alias+"] arg1["+password+"]");*/
if(this.op2KeyStore!=null) {
try {
return this.op2KeyStore.getPrivateKey(alias, password);
}catch(Exception e) {
throw new WSSecurityException(ErrorCode.SECURITY_ERROR,e);
}
}
return super.getPrivateKey(alias, password);
}
/**@Override
public PrivateKey getPrivateKey(X509Certificate x509, CallbackHandler callbackHandler) throws WSSecurityException {
System.out.println("@@@ getPrivateKey X509Certificatw, CALLBACK");
return super.getPrivateKey(x509, callbackHandler);
}*/
@Override
public String getCryptoProvider() {
boolean useBC = (this.useBouncyCastleProviderDirective!=null && this.useBouncyCastleProviderDirective)
||
(MerlinProvider.useBouncyCastleProvider);
if(useBC) {
if(this.truststore!=null && this.truststore.getType()!=null && this.truststore.getType().equalsIgnoreCase(KeystoreType.PKCS11.getNome())) {
useBC=false;
}
if(this.keystore!=null && this.keystore.getType()!=null && this.keystore.getType().equalsIgnoreCase(KeystoreType.PKCS11.getNome())) {
useBC=false;
}
}
if(useBC) {
return org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
}
else {
return super.getCryptoProvider();
}
}
public static void correctProviderName(MessageSecurity messageSecurity){
if(messageSecurity!=null) {
MessageSecurityFlow request = messageSecurity.getRequestFlow();
correctProviderNameRequestFlow(request);
MessageSecurityFlow response = messageSecurity.getResponseFlow();
correctProviderNameResponseFlow(response);
}
}
private static void correctProviderNameRequestFlow(MessageSecurityFlow request){
if(request!=null && request.sizeParameterList()>0) {
for (MessageSecurityFlowParameter param : request.getParameterList()) {
if(param.getNome()!=null && param.getNome().trim().endsWith(KeystoreConstants.PROPERTY_PROVIDER) &&
param.getValore()!=null && KeystoreConstants.OLD_PROVIDER_GOVWAY.equals(param.getValore().trim()) ) {
param.setValore(KeystoreConstants.PROVIDER_GOVWAY);
}
}
}
}
private static void correctProviderNameResponseFlow(MessageSecurityFlow response){
if(response!=null && response.sizeParameterList()>0) {
for (MessageSecurityFlowParameter param : response.getParameterList()) {
if(param.getNome()!=null && param.getNome().trim().endsWith(KeystoreConstants.PROPERTY_PROVIDER) &&
param.getValore()!=null && KeystoreConstants.OLD_PROVIDER_GOVWAY.equals(param.getValore().trim()) ) {
param.setValore(KeystoreConstants.PROVIDER_GOVWAY);
}
}
}
}
}