com.cybersource.ws.client.SecurityUtil Maven / Gradle / Ivy
The newest version!
package com.cybersource.ws.client;
import org.apache.wss4j.common.WSEncryptionPart;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.KeyUtils;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.WSDocInfo;
import org.apache.wss4j.dom.message.WSSecEncrypt;
import org.apache.wss4j.dom.message.WSSecHeader;
import org.apache.wss4j.dom.message.WSSecSignature;
import org.apache.xml.security.Init;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.w3c.dom.Document;
import javax.crypto.KeyGenerator;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Utility class for security related functions like loading p12 key, create signed or encrypted doc,
*/
public class SecurityUtil {
private static final String KEY_FILE_TYPE = "PKCS12";
private static final String FAILED_TO_LOAD_KEY_STORE = "Exception while loading KeyStore";
private static final String FAILED_TO_OBTAIN_PRIVATE_KEY = "Exception while obtaining private key from KeyStore with alias";
private static MessageHandlerKeyStore localKeyStoreHandler = null;
//mapping between IdentityName and Identity
private static ConcurrentHashMap identities = new ConcurrentHashMap();
// By default signature algorithm is set to null and during WSSecSignature build() Signature algorithm will set to "http://www.w3.org/2000/09/xmldsig#rsa-sha1" .
private static final String SIGNATURE_ALGORITHM = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
// By default digest algorithm is set to "http://www.w3.org/2000/09/xmldsig#sha1"
private static final String DIGEST_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#sha256";
private static BouncyCastleProvider bcProvider = new BouncyCastleProvider();
// This is loaded by WSS4J but since we use it lets make sure its here
static {
Security.addProvider(bcProvider);
try {
initKeystore();
//Must initialize xml-security library correctly before use it
Init.init();
} catch (Exception e) {
localKeyStoreHandler=null;
}
}
private static void initKeystore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException{
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(null, null);
localKeyStoreHandler = new MessageHandlerKeyStore();
localKeyStoreHandler.setKeyStore(keyStore);
}
/**
* Method loads the Merchant P12 key.
* IMPORTANT :This change is made based on the assumptions that at point of time , a merchant will have only one P12 Key
*
*CertificateCacheEnabled : If it is true then only first time merchant p12 file will be loaded.
* If it is false then every time merchant p12 file will be loaded.
*
*isValid() method checks : If this method returns true that means existing certificate is valid and reload of merchant p12 file will not happen.
* : If method returns false that means existing certificate is not valid and reload of new merchant p12 file will happen.
*
* @param merchantConfig - Merchant Config
* @param logger - logger instance
* @throws SignException - Signature exception
* @throws SignEncryptException
* @throws ConfigException
*/
public static void loadMerchantP12File(MerchantConfig merchantConfig, Logger logger) throws SignException, SignEncryptException, ConfigException {
Identity identity=identities.get(merchantConfig.getKeyAlias());
if(!merchantConfig.isCertificateCacheEnabled() || identity == null || !(identity.isValid(merchantConfig.getKeyFile(), logger))){
try {
if (localKeyStoreHandler == null)
initKeystore();
} catch (Exception e) {
logger.log(Logger.LT_EXCEPTION,
"SecurityUtil, cannot instantiate class with keystore error. "
+ e.getMessage());
throw new SignException(e.getMessage(), e);
}
if(merchantConfig.isJdkCertEnabled()){
logger.log(Logger.LT_INFO," Loading the certificate from JDK Cert");
SecurityUtil.readJdkCert(merchantConfig,logger);
}
else if(merchantConfig.isCacertEnabled()){
logger.log(Logger.LT_INFO," Loading the certificate from JRE security cacert file");
SecurityUtil.loadJavaKeystore(merchantConfig,logger);
}
else{
logger.log(Logger.LT_INFO,"Loading the certificate from p12 file ");
readAndStoreCertificateAndPrivateKey(merchantConfig, logger);
}
}
}
/**
* Reads the Certificate or Public key and Private from the P12 key .
* @param merchantConfig - Merchant Config details
* @param logger - logger object
* @throws SignException
* @throws SignEncryptException
*/
private static void readAndStoreCertificateAndPrivateKey(MerchantConfig merchantConfig, Logger logger) throws SignException, SignEncryptException {
KeyStore merchantKeyStore;
try {
merchantKeyStore = KeyStore.getInstance(KEY_FILE_TYPE,
bcProvider);
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION, "Exception while instantiating KeyStore");
throw new SignException("Exception while instantiating KeyStore",e);
}
try {
merchantKeyStore.load(new FileInputStream(merchantConfig.getKeyFile()), merchantConfig.getKeyPassword().toCharArray());
} catch (IOException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_LOAD_KEY_STORE + ", '" + merchantConfig.getKeyFilename() + "'");
throw new SignException(FAILED_TO_LOAD_KEY_STORE,e);
} catch (NoSuchAlgorithmException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_LOAD_KEY_STORE + ", '" + merchantConfig.getKeyFilename() + "'");
throw new SignException(FAILED_TO_LOAD_KEY_STORE,e);
} catch (CertificateException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_LOAD_KEY_STORE + ", '" + merchantConfig.getKeyFilename() + "'");
throw new SignException(FAILED_TO_LOAD_KEY_STORE,e);
} catch (ConfigException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_LOAD_KEY_STORE + ", '" + merchantConfig.getKeyFilename() + "'");
throw new SignException(FAILED_TO_LOAD_KEY_STORE,e);
}
// our p12 files do not contain an alias as a normal name, its the common name and serial number
String merchantKeyAlias;
try {
Enumeration enumKeyStore = merchantKeyStore.aliases();
while (enumKeyStore.hasMoreElements()) {
KeyStore.PrivateKeyEntry keyEntry = null;
merchantKeyAlias = (String) enumKeyStore.nextElement();
if (merchantKeyAlias.contains(merchantConfig.getKeyAlias())){
try {
keyEntry = (KeyStore.PrivateKeyEntry) merchantKeyStore.getEntry
(merchantKeyAlias, new KeyStore.PasswordProtection(merchantConfig.getKeyPassword().toCharArray()));
} catch (NoSuchAlgorithmException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_OBTAIN_PRIVATE_KEY + ", '" + merchantConfig.getKeyAlias() + "'");
throw new SignException(FAILED_TO_OBTAIN_PRIVATE_KEY, e);
} catch (UnrecoverableEntryException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_OBTAIN_PRIVATE_KEY + ", '" + merchantConfig.getKeyAlias() + "'");
throw new SignException(FAILED_TO_OBTAIN_PRIVATE_KEY, e);
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_OBTAIN_PRIVATE_KEY + ", '" + merchantConfig.getKeyAlias() + "'");
throw new SignException(FAILED_TO_OBTAIN_PRIVATE_KEY, e);
}
Identity identity = new Identity(merchantConfig,(X509Certificate) keyEntry.getCertificate(),keyEntry.getPrivateKey(),logger);
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getKeyAlias(), identity);
continue;
}
Identity identity = new Identity(merchantConfig, (X509Certificate) merchantKeyStore.getCertificate(merchantKeyAlias));
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getName(), identity);
}
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_OBTAIN_PRIVATE_KEY + ", '" + merchantConfig.getKeyAlias() + "'");
throw new SignException(FAILED_TO_OBTAIN_PRIVATE_KEY, e);
}
}
/**
* Create signed encrypted document
* @param signedDoc
* @param merchantId
* @param logger
* @return Document
* @throws SignEncryptException
* @throws SignException
*/
public static Document handleMessageCreation(Document signedDoc, String merchantId, Logger logger) throws SignEncryptException, SignException{
logger.log(Logger.LT_INFO, "Encrypting Signed doc ...");
WSSecHeader secHeader = new WSSecHeader(signedDoc);
try {
secHeader.insertSecurityHeader();
} catch (WSSecurityException e) {
logger.log(Logger.LT_EXCEPTION, "Exception while adding document in soap securiy header for MLE");
throw new SignException(e);
}
WSSecEncrypt encrBuilder = new WSSecEncrypt(secHeader);
//Set the user name to get the encryption certificate.
//The public key of this certificate is used, thus no password necessary. The user name is a keystore alias usually.
String serverAlias = getServerAlias(identities);
encrBuilder.setUserInfo(identities.get(serverAlias).getKeyAlias());
/*This is to reference a public key or certificate when signing or encrypting a SOAP message.
*The following valid values for these configuration items are:
*IssuerSerial (default),DirectReference[BST],X509KeyIdentifier,Thumbprint,SKIKeyIdentifier,KeyValue (signature only),EncryptedKeySHA1 (encryption only)
*/
encrBuilder.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
//This encryption algorithm is used to encrypt the data.
encrBuilder.setSymmetricEncAlgorithm(WSConstants.AES_256);
//Sets the algorithm to encode the symmetric key. Default is the WSConstants.KEYTRANSPORT_RSAOEP algorithm.
//encrBuilder.setKeyEnc(WSConstants.KEYTRANSPORT_RSAOEP);
//Create signed document
//Document signedDoc = createSignedDoc(workingDocument,senderAlias,password,secHeader);
Document signedEncryptedDoc;
try {
//Builds the SOAP envelope with encrypted Body and adds encrypted key.
// If no external key (symmetricalKey) was set ,generate an encryption
// key (session key) for this Encrypt element. This key will be
// encrypted using the public key of the receiver
KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_256);
signedEncryptedDoc = encrBuilder.build(localKeyStoreHandler, keyGen.generateKey());
} catch (WSSecurityException e) {
logger.log(Logger.LT_EXCEPTION, "Failed while encrypting signed request for , '" + merchantId + "'" + " with " + serverAlias);
throw new SignEncryptException("Failed while encrypting signed request for , '" + merchantId + "'" + " with " + serverAlias, e);
}
encrBuilder.prependToHeader();
return signedEncryptedDoc;
}
/**
* Create signed document
* @param workingDocument
* @param keyAlias
* @param password
* @param logger
* @return Document
* @throws SignException
*/
public static Document createSignedDoc(Document workingDocument,String keyAlias, String password,Logger logger) throws SignException {
logger.log(Logger.LT_INFO, "Signing request...");
// long startTime = System.nanoTime();
WSSecHeader secHeader = new WSSecHeader(workingDocument);
try {
secHeader.insertSecurityHeader();
} catch (WSSecurityException e) {
logger.log(Logger.LT_EXCEPTION,
"Exception while signing XML document");
throw new SignException(e);
}
WSSecSignature sign = new WSSecSignature(secHeader);
sign.setUserInfo(keyAlias, password);
sign.setDigestAlgo(DIGEST_ALGORITHM);
sign.setSignatureAlgorithm(SIGNATURE_ALGORITHM);
sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
sign.setUseSingleCertificate(true);
//
sign.setWsDocInfo(new WSDocInfo(workingDocument));
//Set which parts of the message to encrypt/sign.
WSEncryptionPart msgBodyPart = new WSEncryptionPart(WSConstants.ELEM_BODY, WSConstants.URI_SOAP11_ENV, "");
try {
sign.addReferencesToSign(Collections.singletonList(msgBodyPart));
Document document = sign.build(localKeyStoreHandler);
// System.out.println("SecurityUtil.createSignedDoc time taken to sign the request is " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
return document;
} catch (WSSecurityException e) {
logger.log(Logger.LT_EXCEPTION, "Failed while signing request for , '" + keyAlias + "'");
throw new SignException(e.getMessage(),e);
}
}
/**
* read jdk certificates from cacerts file
* @param merchantConfig
* @param logger
* @throws SignEncryptException
* @throws SignException
*/
public static void readJdkCert(MerchantConfig merchantConfig, Logger logger)
throws SignEncryptException, SignException {
KeyStore keystore;
try {
FileInputStream is = new FileInputStream(merchantConfig.getKeyFile());
keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, merchantConfig.getKeyPassword().toCharArray());
} catch (Exception e) {
logger.log(Logger.LT_EXCEPTION, "Failed to load the key , '" + merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
}
String merchantKeyAlias;
try {
Enumeration enumKeyStore = keystore.aliases();
if (!enumKeyStore.hasMoreElements()) {
throw new SignException("Empty Keystore or Missing Certificate ");
}
while (enumKeyStore.hasMoreElements()) {
KeyStore.PrivateKeyEntry keyEntry;
merchantKeyAlias = (String) enumKeyStore.nextElement();
if (merchantKeyAlias.contains(merchantConfig.getKeyAlias())) {
try {
keyEntry = (KeyStore.PrivateKeyEntry) keystore.getEntry(merchantKeyAlias,
new KeyStore.PasswordProtection(merchantConfig.getKeyPassword().toCharArray()));
} catch (NoSuchAlgorithmException e) {
logger.log(Logger.LT_EXCEPTION,
FAILED_TO_OBTAIN_PRIVATE_KEY + ", '"
+ merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
} catch (UnrecoverableEntryException e) {
logger.log(Logger.LT_EXCEPTION,
FAILED_TO_OBTAIN_PRIVATE_KEY + ", '"
+ merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION,
FAILED_TO_OBTAIN_PRIVATE_KEY + ", '"
+ merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
}
Identity identity = new Identity(merchantConfig, (X509Certificate) keyEntry.getCertificate(),
keyEntry.getPrivateKey(), logger);
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getKeyAlias(), identity);
continue;
}
Identity identity = new Identity(merchantConfig,
(X509Certificate) keystore.getCertificate(merchantKeyAlias));
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getName(), identity);
}
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION, FAILED_TO_OBTAIN_PRIVATE_KEY + ", '"
+ merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
}
}
private static void loadJavaKeystore(MerchantConfig merchantConfig, Logger logger)
throws SignException, SignEncryptException, ConfigException {
FileInputStream is = null;
try {
is = new FileInputStream(merchantConfig.getKeyFile());
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, merchantConfig.getCacertPassword().toCharArray());
java.security.cert.Certificate[] cert = keystore.getCertificateChain(merchantConfig.getKeyAlias());
if (cert == null) {
throw new SignException("Empty Keystore or Missing Certificate ");
}
PrivateKey key;
try {
key = (PrivateKey) keystore.getKey(merchantConfig.getKeyAlias(),
merchantConfig.getKeyAlias().toCharArray());
} catch (UnrecoverableKeyException e) {
logger.log(Logger.LT_EXCEPTION, "Exception while obtaining private key from KeyStore with alias, '"
+ merchantConfig.getKeyAlias() + "'");
throw new SignException(e);
}
Identity identity;
for (java.security.cert.Certificate certificate : cert) {
if (merchantConfig.getKeyAlias().equals(keystore.getCertificateAlias(certificate))) {
identity = new Identity(merchantConfig, (X509Certificate) certificate, key, logger);
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getKeyAlias(), identity);
} else {
identity = new Identity(merchantConfig, (X509Certificate) certificate);
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getName(), identity);
}
}
java.security.cert.Certificate serverCert = keystore.getCertificate(getServerAlias(identities));
if (serverCert == null) {
throw new SignException("Missing Server Certificate ");
}
identity = new Identity(merchantConfig, (X509Certificate) serverCert);
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
identities.put(identity.getName(), identity);
}
catch (java.security.cert.CertificateException e) {
logger.log(Logger.LT_EXCEPTION, "Unable to load the certificate," + merchantConfig.getKeyFilename() + "'");
throw new SignException(e);
} catch (NoSuchAlgorithmException e) {
logger.log(Logger.LT_EXCEPTION, "Unable to find the certificate with the specified algorithm");
throw new SignException(e);
} catch (FileNotFoundException e) {
logger.log(Logger.LT_EXCEPTION, "File Not found ");
throw new SignException(e);
} catch (KeyStoreException e) {
logger.log(Logger.LT_EXCEPTION,
"Exception while obtaining private key from KeyStore" + merchantConfig.getKeyFilename() + "'");
throw new SignException(e);
} catch (IOException e) {
logger.log(Logger.LT_EXCEPTION,
FAILED_TO_LOAD_KEY_STORE + ", '" + merchantConfig.getKeyFilename() + "'");
throw new SignException(e);
} finally {
if (null != is)
try {
is.close();
} catch (IOException e) {
logger.log(Logger.LT_EXCEPTION,
"Exception while closing FileStream, '" + merchantConfig.getKeyFilename() + "'");
}
}
}
protected static String getServerAlias(Map identitiesMapper) {
String serverAlias = Utility.SERVER_ALIAS;
if(!identitiesMapper.containsKey(serverAlias)) {
if(identitiesMapper.containsKey(serverAlias.toLowerCase())) {
serverAlias = serverAlias.toLowerCase();
} else if(identitiesMapper.containsKey(serverAlias.toUpperCase())) {
serverAlias = serverAlias.toUpperCase();
} else {
for(String identityKey :identitiesMapper.keySet()) {
if(identityKey.equalsIgnoreCase(serverAlias)) {
serverAlias = identityKey;
break;
}
}
}
}
return serverAlias;
}
}