diff --git a/domain.md b/domain.md new file mode 100644 index 0000000..c8be5c4 --- /dev/null +++ b/domain.md @@ -0,0 +1,207 @@ +# Externalize API Gateway domain certificates + +## Admin Node manager + +- Create a CSR file +```bash +./gen_domain_cert.py --domain-id=dss --out=csr --O=Axway --OU=DSS --C=US --ST=AZ --L=Scottsdale --pass-file=rootcerts/pass.txt +``` +command creates a folder named dss under apigw-emt-scripts-2.1.0-SNAPSHOT/certs/ with following files + + - dss.csr + - dss-key.pem + +- Create CA CSR, certificate and key +```bash +openssl genrsa -aes256 -out CA.key 2048 +openssl req -new -sha256 -key CA.key -out CA.csr -subj "/C=US/ST=AZ/L=Scottsdale/O=AXWAY/CN=CACERTIFICATE" +openssl x509 -signkey CA.key -in CA.csr -req -days 3650 -out CA.pem +``` +command creates following files + + - CA.key + - CA.csr + - CA.pem + - CA.srl + +- Create a file openssl.cnf with following content + +```text +[policy_any] +domainComponent = optional +organizationalUnitName = optional +commonName = supplied + +[req] +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[x509_extensions] + +[domain_extensions] +basicConstraints = CA:TRUE, pathlen:0 +keyUsage = digitalSignature, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign + +[admin_node_manager_extensions] +basicConstraints = CA:FALSE +keyUsage = digitalSignature, dataEncipherment, keyAgreement, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth, 1.3.6.1.4.1.17998.10.1.1.2.1, 1.3.6.1.4.1.17998.10.1.1.2.2 +subjectAltName = @alt_names + +[node_manager_extensions] +basicConstraints = CA:FALSE +keyUsage = digitalSignature, dataEncipherment, keyAgreement +extendedKeyUsage = serverAuth, clientAuth, 1.3.6.1.4.1.17998.10.1.1.2.1 +subjectAltName = @alt_names + +[gateway_extensions] +basicConstraints = CA:FALSE +keyUsage = digitalSignature, dataEncipherment, keyAgreement +extendedKeyUsage = serverAuth, clientAuth, 1.3.6.1.4.1.17998.10.1.1.2.3 +subjectAltName = @alt_names + +[alt_names] + +DNS.1 = localhost +IP.1 = 127.0.0.1 + +``` + +- Sign dss.csr with CA certificate and key using openssl configuration + +```bash +openssl x509 -req -days 360 -in dss.csr -CA CA.pem -CAkey CA.key -CAcreateserial -out signedbyCA.crt -sha256 -extensions admin_node_manager_extensions -extfile openssl.cnf +``` + +- Create a P12 file from CA signed certificate and key file + +```bash +openssl pkcs12 -export -in signedbyCA.crt -inkey dss-key.pem -out domain.p12 -chain -CAfile CA.pem -name 'topology-cert' +``` +**alias name should be 'topology-cert'** + +- Prepare Admin Node Manager fed file + + - Export Admin Node manager fed from classic installation, remove existing topology-cert and change remove / rename port name without blank space (e.g sslport) + + - Import loadable module + Policystudio using File -> Import -> Import Custom filters -> select apim-policy-password-cert-env/src/main/resources/typeSet.xml. + + - Export fed file + +- Configure environment variable (docker-compose / kubernetes deployment) + +```yaml + # docker-compose.yaml example + environment: + EMT_TOPOLOGY_LOG_ENABLED: 'true' + EMT_TOPOLOGY_LOG_DEST: 3 + certandkey_sslport: /opt/Axway/apigateway/domain.p12 + certandkeypassword_sslport: changeme + certandkeymtls_sslport: 'true' +``` + +- comment lines related to certificate generation in apigw-emt-scripts-2.1.0-SNAPSHOT/Dockerfiles/emt-nodemanager/scripts/setup_emt_nodemanager.py +```python + try: + # self._generateTopologyCert(nmHandler) + # self._storeCertsInEntityStore(nmHandler) + + localNodeManager, topology, topologyParams = self._createTopologyJson() + # print("Enabling SSL on management interface") + # nmHandler.enableSSLInterface(True, TopologyCertificate.CERT_ALIAS, topologyParams) + # self._updateConfigFiles(localNodeManager, topology) + + # Delete the cert generation temp directory + shutil.rmtree(nmHandler.tempCertPath) + + except Exception, e: + _fail("Error generating topology cert: %s" % e) +``` +- Build Admin Node Manger Image + +```bash +./build_anm_image.py --default-cert --default-user --parent-image=apigw-base --merge-dir=/Users/rnatarajan/APIM/apigw-emt-scripts-2.1.0-SNAPSHOT/apigateway --fed extanm.fed --out-image=admin-node-manager-ext-ca-env:latest +``` +**param default-cert is not used, but it is a mandatory argument for building anm image** + +## Configure Gateway + +- Create a CSR file + +```bash +./gen_domain_cert.py --domain-id=dssgateway --out=csr --O=Axway --OU=DSS --C=US --ST=AZ --L=Scottsdale --pass-file=rootcerts/pass.txt +``` +command creates a folder named dssgateway under apigw-emt-scripts-2.1.0-SNAPSHOT/certs/ with following files + + - dssgateway.csr + - dssgateway-key.pem +- Copy CA.pem, CA.key, CA.srl and openssl files from dss folder to dssgateway folder + +```bash +dssgateway$cp ../dss/CA.pem . +dssgateway$cp ../dss/CA.key . +dssgateway$cp ../dss/CA.srl . +dssgateway$cp ../dss/openssl.cnf . +``` +- Sign dss.csr with CA certificate and key using openssl configuration + +```bash +openssl x509 -req -days 360 -in dssgateway.csr -CA CA.pem -CAkey CA.key -CAcreateserial -out signedbygatewayCA.crt -sha256 -extensions gateway_extensions -extfile openssl.cnf +``` + +command creates a file named signedbygatewayCA.crt + +- Create p12 file **without password** + +```bash +openssl pkcs12 -export -in signedbygatewayCA.crt -inkey dssgateway-key.pem -out topology.p12 -chain -CAfile CA.pem -name 'topology-cert' -passout pass: +``` + +- Prepare Admin Node Manager fed file + + - Import loadable module + Policystudio using File -> Import -> Import Custom filters -> select apim-policy-password-cert-env/src/main/resources/typeSet.xml. + + - Export fed file + +- Configure environment variable (docker-compose / kubernetes deployment) + +```yaml + # docker-compose.yaml example + # Mandatory + volumes: + - /Users/rnatarajan/APIM/apigw-emt-scripts-2.1.0-SNAPSHOT/certs/dssgateway/p12:/opt/Axway/apigateway/groups/certs/ + environment: + EMT_ANM_HOSTS: nodemgr:8090 + CASS0: host.docker.internal + CASS_HOST: host.docker.internal + CASS_USER: dba + CASS_PASSWORD: super + CASS_KEYSPACE: axwayapim + # We should use same path + gatewaytoplogycertandkey_domain: /opt/Axway/apigateway/groups/certs/topology.p12 + gatewaytoplogycertandkeypassword_domain: '' +``` + +- comment lines related to certificate generation in apigw-emt-scripts-2.1.0-SNAPSHOT/Dockerfiles/emt-gateway/scripts/setup_emt_instance.py + +```python +def _setup(): + _mergePolAndEnvToFed() + _installCustomFedFile() + _setupApiManager() + _createInstanceDirStructure() + _customizeInstallation() + _checkLicense() + + # ch = CertHandler() + # ch.generateCert() + # ch.enableSSLInterface() +``` + +- Build API Gateway Image +```bash +./build_gw_image.py --default-cert --license=/Users/rnatarajan/APIM/apigw-emt-scripts-2.1.0-SNAPSHOT/licenses/apim.lic --parent-image=apigw-base --merge-dir=/Users/rnatarajan/APIM/apigw-emt-scripts-2.1.0-SNAPSHOT/apigateway --fed=container_env.fed --out-image=apim-cert-ca-env:latest +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 53f4357..887a406 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.axway apim-env-module - 1.1.3 + 1.1.4 apim-env-module https://axway.com @@ -48,7 +48,6 @@ ${apim.lib.path}/vordel-apigateway-7.7.0.20201130-5.jar - vordel-core-runtime vordel-core-runtime system @@ -86,12 +85,12 @@ org.apache.logging.log4j log4j-api - 2.11.2 + 2.13.3 org.apache.logging.log4j log4j-core - 2.13.2 + 2.13.3 diff --git a/src/main/java/com/axway/CertHelper.java b/src/main/java/com/axway/CertHelper.java index d7bba6d..5981218 100644 --- a/src/main/java/com/axway/CertHelper.java +++ b/src/main/java/com/axway/CertHelper.java @@ -7,8 +7,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.Base64; -import java.util.Enumeration; +import java.util.*; public class CertHelper { @@ -63,18 +62,25 @@ public PKCS12 parseP12(String content, char[] password) throws KeyStoreException } - public X509Certificate parseX509(String base64EncodedCert) throws CertificateException, FileNotFoundException { + public List parseX509(String base64EncodedCertOrFilePath) throws CertificateException, FileNotFoundException { - File file = new File(base64EncodedCert); + File file = new File(base64EncodedCertOrFilePath); InputStream inputStream = null; if(file.exists()){ inputStream = new FileInputStream(file); }else { - inputStream = new ByteArrayInputStream(base64EncodedCert.getBytes()); + inputStream = new ByteArrayInputStream(base64EncodedCertOrFilePath.getBytes()); } CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) certificateFactory.generateCertificate(inputStream); + Collection parsedCertificates = certificateFactory.generateCertificates(inputStream); + List certificates = new ArrayList<>(); + + for (Certificate certificate: parsedCertificates) { + certificates.add ((X509Certificate)certificate); + } + return certificates; + // return (X509Certificate) certificateFactory.generateCertificate(inputStream); } } diff --git a/src/main/java/com/axway/ExternalConfigLoader.java b/src/main/java/com/axway/ExternalConfigLoader.java index 55c28c1..fe082d0 100644 --- a/src/main/java/com/axway/ExternalConfigLoader.java +++ b/src/main/java/com/axway/ExternalConfigLoader.java @@ -1,5 +1,6 @@ package com.axway; +import com.vordel.common.Config; import com.vordel.common.crypto.PasswordCipher; import com.vordel.config.ConfigContext; import com.vordel.config.LoadableModule; @@ -25,9 +26,9 @@ public class ExternalConfigLoader implements LoadableModule { private static final Logger log = LogManager.getLogger(ExternalConfigLoader.class); private final CertHelper certHelper = new CertHelper(); + private final ExternalInstanceDomainCert externalInstanceDomainCert = new ExternalInstanceDomainCert(); private PasswordCipher passwordCipher; - @Override public void load(LoadableModule arg0, String arg1) { log.info("loading Password and Certificate Environment variable Module"); @@ -98,11 +99,12 @@ private void updatePassword(EntityStore entityStore) { String radiusShorthandKey = shorthandKey + "/[RadiusServer]host=" + host + ",port=" + port; updatePasswordField(entityStore, radiusShorthandKey, "secret", passwordValue, null); } - } else if (key.startsWith("cert_")) { try { - X509Certificate certificate = certHelper.parseX509(passwordValue); - importPublicCertificate(certificate, entityStore); + List certificates = certHelper.parseX509(passwordValue); + for (X509Certificate certificate:certificates) { + importPublicCertificate(certificate, entityStore); + } } catch (CertificateException | FileNotFoundException e) { Trace.error("Unable to add the certs from Environment variable", e); } @@ -120,12 +122,21 @@ private void updatePassword(EntityStore entityStore) { } } else if (key.startsWith("cassandraCert")) { try { - X509Certificate certificate = certHelper.parseX509(passwordValue); - String alias = importPublicCertificate(certificate, entityStore); - if(alias != null) { - String escapedAlias = ShorthandKeyFinder.escapeFieldValue(alias); - updateCassandraCert(entityStore, escapedAlias); + List certificates = certHelper.parseX509(passwordValue); + int index = 0; + for (X509Certificate certificate:certificates) { + String alias = importPublicCertificate(certificate, entityStore); + if(alias != null) { + // String escapedAlias = ShorthandKeyFinder.escapeFieldValue(alias); + //updateCassandraCert(entityStore, escapedAlias); + if(index == 0) + updateCassandraCert(entityStore, alias, false); + else + updateCassandraCert(entityStore, alias, true); + index++; + } } + } catch (CertificateException | FileNotFoundException e) { Trace.error("Unable to add Cassandra certificate from Environment variable", e); } @@ -133,9 +144,10 @@ private void updatePassword(EntityStore entityStore) { try { Trace.info("Updating SSL interface certificate and key"); char[] password = System.getenv("certandkeypassword" + "_" + filterName).toCharArray(); - String alias = importP12(entityStore, passwordValue, password); - Trace.info("P12 file alias name :" + alias); - configureP12(entityStore, filterName, alias); + String mTLS = System.getenv("certandkeymtls" + "_" + filterName); + PKCS12 pkcs12 = importP12(entityStore, passwordValue, password); + Trace.info("P12 file alias name :" + pkcs12.getAlias()); + configureP12(entityStore, filterName, pkcs12, mTLS); } catch (Exception e) { Trace.error("Unable to add the p12 from Environment variable", e); } @@ -143,14 +155,34 @@ private void updatePassword(EntityStore entityStore) { try { Trace.info("Updating Connect to URL client Auth certificate and key"); char[] password = System.getenv("connecttourlcertandkeypassword" + "_" + filterName).toCharArray(); - String alias = importP12(entityStore, passwordValue, password); + String alias = importP12(entityStore, passwordValue, password).getAlias(); Trace.info("P12 file alias name :" + alias); connectToURLConfigureP12(entityStore, filterName, alias); } catch (Exception e) { Trace.error("Unable to add the p12 from Environment variable", e); } + } else if (key.startsWith("gatewaytoplogycertandkey_")) { + try { + Trace.info("Updating Gateway topology certificate"); + char[] password = System.getenv("gatewaytoplogycertandkeypassword" + "_" + filterName).toCharArray(); + File file = new File(passwordValue); + PKCS12 pkcs12; + if(file.exists()){ + pkcs12 = certHelper.parseP12(file, password); + }else { + pkcs12 = certHelper.parseP12(passwordValue, password); + } + File gatewayConfDir = new File(Config.getVDir("VINSTDIR"), "conf"); + File certsXml = new File(gatewayConfDir, "certs.xml"); + String caAlias = externalInstanceDomainCert.certsFile(pkcs12, certsXml); + File mgmtXml = new File(gatewayConfDir, "mgmt.xml"); + externalInstanceDomainCert.updateMgmtFile(mgmtXml, caAlias); + + } catch (Exception e) { + Trace.error("Unable to add the p12 from Environment variable", e); } } + } List credentials = parseCred(ldap, "ldap"); if (!credentials.isEmpty()) { @@ -171,7 +203,6 @@ private void updatePassword(EntityStore entityStore) { for (Credential credential : credentials) { updateSMTP(entityStore, credential); updateAlertSMTP(entityStore, credential); - } } } @@ -302,17 +333,13 @@ private void updateAlertSMTP(EntityStore entityStore, Credential credential) { } } - private void updateCassandraCert(EntityStore entityStore, String escapedAlias) { + private void updateCassandraCert(EntityStore entityStore, String alias, boolean append) { String shorthandKey = "/[CassandraSettings]name=Cassandra Settings"; Entity entity = getEntity(entityStore, shorthandKey); boolean useSSL = entity.getBooleanValue("useSSL"); if (useSSL) { - //String certPlaceHolder = ""; - Entity certEntity = getCertEntity(entityStore, escapedAlias); - PortableESPK portableESPK = PortableESPK.toPortableKey(entityStore, certEntity.getPK()); - // PortableESPK portableESPK = getCertEntity(entityStore, escapedAlias); - entity.setReferenceField("sslTrustedCerts", portableESPK); - entityStore.updateEntity(entity); + String filedName = "sslTrustedCerts"; + updateCertEntity(entityStore, entity, alias, filedName, append); } } @@ -348,7 +375,6 @@ private String importPublicCertificate(X509Certificate certificate, EntityStore Entity certEntity = getCertEntity(entityStore, escapedAlias); Trace.info("Alias :" + alias + "Escaped alias :"+ escapedAlias); - if (certEntity == null) { Trace.info("Adding cert"); certEntity = EntityStoreDelegate.createDefaultedEntity(entityStore, "Certificate"); @@ -363,18 +389,16 @@ private String importPublicCertificate(X509Certificate certificate, EntityStore certEntity.setBinaryValue("content", certificate.getEncoded()); entityStore.updateEntity(certEntity); } - return escapedAlias; + return alias; } catch (CertificateException e) { Trace.error("Unable to add the certs from Environment variable", e); } return null; } - private void configureP12(EntityStore entityStore, String name, String alias) { + private void configureP12(EntityStore entityStore, String name, PKCS12 pkcs12, String mTLS) { String shorthandKey = "/[NetService]name=Service/[HTTP]**/[SSLInterface]name=" + name; - //ShorthandKeyFinder shorthandKeyFinder = new ShorthandKeyFinder(entityStore); - //List entities = shorthandKeyFinder.getEntities(shorthandKey); List entities = getEntities(entityStore, shorthandKey); if (entities.isEmpty()) { Trace.error("Listener interface is not available"); @@ -385,7 +409,34 @@ private void configureP12(EntityStore entityStore, String name, String alias) { } Entity entity = entities.get(0); String fieldName = "serverCert"; - updateP12Cert(entityStore, entity, alias, fieldName); + String alias = pkcs12.getAlias(); + updateCertEntity(entityStore, entity, alias, fieldName, false); + Trace.info("Mutual auth flag : "+ mTLS); + if(mTLS != null && mTLS.equalsIgnoreCase("true")){ + String clientAuth = entity.getStringValue("clientAuth"); + Trace.info("Mutual auth configured with flag : "+ clientAuth); + if(clientAuth.equals("required") || clientAuth.equals("optional")){ + trustRootAndIntermediateCerts(entityStore, entity, pkcs12 ); + } + } + } + + private void trustRootAndIntermediateCerts(EntityStore entityStore, Entity entity, PKCS12 pkcs12){ + Certificate[] certificates = pkcs12.getCertificates(); + Trace.info("Trusting additional certs for mutual auth"); + Trace.info("Total certificates : "+ certificates.length); + for (int i = 1; i < certificates.length; i++) { + X509Certificate certificate = (X509Certificate) certificates[i]; + Principal principal = certificate.getSubjectDN(); + final String alias = principal.getName(); + Trace.info("Trusting cert :"+ alias); + String fieldName = "caCert"; + if( i == 1) + updateCertEntity(entityStore, entity, alias, fieldName, false); + else + // Trust more than one certificate for mutual auth + updateCertEntity(entityStore, entity, alias, fieldName, true); + } } private List getEntities(EntityStore entityStore, String shorthandKey){ @@ -393,14 +444,32 @@ private List getEntities(EntityStore entityStore, String shorthandKey){ return shorthandKeyFinder.getEntities(shorthandKey); } - private void updateP12Cert(EntityStore entityStore, Entity entity, String alias, String fieldName){ + private void updateCertEntity(EntityStore entityStore, Entity entity, String alias, String fieldName, boolean append){ String escapedAlias = ShorthandKeyFinder.escapeFieldValue(alias); Entity certEntity = getCertEntity(entityStore, escapedAlias); - //Trace.info("Certificate entity set to listener interface "+ certEntity); + // Trace.info("Certificate entity set to listener interface "+ certEntity); PortableESPK portableESPK = PortableESPK.toPortableKey(entityStore, certEntity.getPK()); //Trace.info("Portable : " + portableESPK); - entity.setReferenceField(fieldName, portableESPK); + if(append) { + Field field = entity.getField(fieldName); + List values = field.getValueList(); + List cloneVales = new ArrayList<>(values); + for (Value value : cloneVales) { + PortableESPK espk = (PortableESPK) value.getRef(); + String certStoreDistinguishedName = espk.getFieldValueOfReferencedEntity("dname"); + Trace.info(" alias name from Gateway Cert store :" + certStoreDistinguishedName); + if (certStoreDistinguishedName.equals(alias)) { + Trace.info("Removing existing certs" + alias); + values.remove(value); + } + Trace.info("adding " + alias); + values.add(new Value(portableESPK)); + } + field.setValues(values); + }else { + entity.setReferenceField(fieldName, portableESPK); + } entityStore.updateEntity(entity); } @@ -418,7 +487,7 @@ private void connectToURLConfigureP12(EntityStore entityStore, String name, Stri } Entity entity = entities.get(0); String fieldName = "sslUsers"; - updateP12Cert(entityStore, entity, alias, fieldName); + updateCertEntity(entityStore, entity, alias, fieldName, false); } private Entity getCertEntity(EntityStore entityStore, String alias) { @@ -434,7 +503,7 @@ private Entity getCertEntity(EntityStore entityStore, String alias) { } - private String importP12(EntityStore entityStore, String cert, char[] password) throws Exception { + private PKCS12 importP12(EntityStore entityStore, String cert, char[] password) throws Exception { PKCS12 pkcs12; File file = new File(cert); @@ -451,7 +520,6 @@ private String importP12(EntityStore entityStore, String cert, char[] password) Trace.info("Escaped Certificate alias name : " + escapedAlias); // Trace.info("Certificate Entity received from entity store : "+ certEntity); if (certEntity != null) { - //certEntity.setBinaryValue(); //Updates the existing certificate in the certstore Trace.info("Updating existing certificate"); for (int i = 0; i < certificates.length; i++) { @@ -465,15 +533,12 @@ private String importP12(EntityStore entityStore, String cert, char[] password) X509Certificate certificate = (X509Certificate) certificates[i]; importPublicCertificate(certificate, entityStore); } - } - } else { ESPK rootPK = entityStore.getRootPK(); EntityType group = entityStore.getTypeForName("Certificates"); Collection groups = entityStore.listChildren(rootPK, group); certEntity = EntityStoreDelegate.createDefaultedEntity(entityStore, "Certificate"); - for (int i = 0; i < certificates.length; i++) { if (i == 0) { Trace.info("Importing Leaf certificate"); @@ -490,9 +555,8 @@ private String importP12(EntityStore entityStore, String cert, char[] password) importPublicCertificate(certificate, entityStore); Trace.info("Imported root / intermediate certificate"); } - } } - return alias; + return pkcs12; } } diff --git a/src/main/java/com/axway/ExternalInstanceDomainCert.java b/src/main/java/com/axway/ExternalInstanceDomainCert.java new file mode 100644 index 0000000..00993ad --- /dev/null +++ b/src/main/java/com/axway/ExternalInstanceDomainCert.java @@ -0,0 +1,170 @@ +package com.axway; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Base64; + +public class ExternalInstanceDomainCert { + + private static final Logger log = LogManager.getLogger(ExternalInstanceDomainCert.class); + + + public final static String LINE_SEPARATOR = System.getProperty("line.separator"); + private Base64.Encoder encoder; + + public ExternalInstanceDomainCert() { + encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes()); + } + + + // File certsXml = new File(Config.getVDir("VINSTDIR"), "conf"); + // File certsXml = new File("src/"); + // certsXml = new File(certsXml, "certs.xml"); + + + public void updateMgmtFile(File mgmtFile, String CAAlias) throws ParserConfigurationException, IOException, SAXException, TransformerException { + if (mgmtFile.exists()) { + log.info("Management file mgmt.xml exists"); + } + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(mgmtFile); + NodeList nodeList = document.getElementsByTagName("SSLInterface"); + Element sslInterface = (Element) nodeList.item(0); + NodeList trustedCA = sslInterface.getElementsByTagName("TrustedCA"); + if (trustedCA.getLength() == 0) { + Element trustedCAElement = document.createElement("TrustedCA"); + trustedCAElement.setAttribute("cert", CAAlias); + sslInterface.appendChild(trustedCAElement); + } + sslInterface.setAttribute("address", "*"); + NodeList verifyIsLocalNodeManager = sslInterface.getElementsByTagName("VerifyIsLocalNodeManager"); + if (verifyIsLocalNodeManager.getLength() > 0) { + Node node = verifyIsLocalNodeManager.item(0); + sslInterface.removeChild(node); + } + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + FileWriter writer = new FileWriter(mgmtFile); + StreamResult result = new StreamResult(writer); + transformer.transform(source, result); +// byte[] data = Files.readAllBytes(Paths.get(mgmtFile.getAbsolutePath())); +// System.out.println(new String(data)); + + } + + public String certsFile(PKCS12 pkcs12, File certsXml) throws IOException, CertificateException { + + String CAAlias = null; + if (certsXml.exists()) { + log.info("Management file certs.xml exists"); + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(""); + PrivateKey privateKey = pkcs12.getPrivateKey(); + + String privateKeyEncoded = encoder.encodeToString(privateKey.getEncoded()); + Certificate[] certificates = pkcs12.getCertificates(); + for (Certificate certificate : certificates) { + PublicKey publicKey = certificate.getPublicKey(); + byte[] data = "test".getBytes(); + byte[] digitalSignature = signData(data, privateKey); + if (verifySignature(data, publicKey, digitalSignature)) { + createCertificateElement(stringBuilder, pkcs12.getAlias()); + createPublicKeyElement(stringBuilder, certificate); + stringBuilder.append(""); + stringBuilder.append(privateKeyEncoded); + stringBuilder.append(""); + } else { + X509Certificate cert = (X509Certificate) certificate; + CAAlias = cert.getSubjectDN().getName(); + createCertificateElement(stringBuilder, CAAlias); + createPublicKeyElement(stringBuilder, certificate); + } + endCertificateElement(stringBuilder); + } + stringBuilder.append(""); + FileOutputStream fileOutputStream = null; + + try { + fileOutputStream = new FileOutputStream(certsXml); + fileOutputStream.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8)); + fileOutputStream.close(); + } finally { + if (fileOutputStream != null) { + fileOutputStream.close(); + } + } + return CAAlias; + } + + public void createCertificateElement(StringBuilder stringBuilder, String alias) { + stringBuilder.append(""); + } + + public void endCertificateElement(StringBuilder stringBuilder) { + stringBuilder.append(""); + } + + public void createPublicKeyElement(StringBuilder stringBuilder, Certificate certificate) throws CertificateEncodingException { + stringBuilder.append(""); + stringBuilder.append(encoder.encodeToString(certificate.getEncoded())); + stringBuilder.append(""); + + } + + + public static byte[] signData(byte[] data, PrivateKey key) { + Signature signer = null; + try { + signer = Signature.getInstance("SHA256withRSA"); + signer.initSign(key); + signer.update(data); + return (signer.sign()); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { + e.printStackTrace(); + return null; + } + } + + public static boolean verifySignature(byte[] data, PublicKey key, byte[] sig) { + Signature signer = null; + try { + signer = Signature.getInstance("SHA256withRSA"); + signer.initVerify(key); + signer.update(data); + return (signer.verify(sig)); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { + e.printStackTrace(); + return false; + } + + } +} diff --git a/src/test/java/com/axway/CertHelperTest.java b/src/test/java/com/axway/CertHelperTest.java index cee12a4..1994e1e 100644 --- a/src/test/java/com/axway/CertHelperTest.java +++ b/src/test/java/com/axway/CertHelperTest.java @@ -11,6 +11,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.List; public class CertHelperTest { @@ -69,11 +70,27 @@ public void testX509() { try { - X509Certificate certificate = certHelper.parseX509(cert); + X509Certificate certificate = certHelper.parseX509(cert).get(0); String name = certificate.getSubjectDN().getName(); System.out.println(name); } catch (CertificateException | FileNotFoundException e) { e.printStackTrace(); } } + + @Test + public void testCertChain() { + + try { + + List certificates = certHelper.parseX509("src/test/resources/certchain.pem"); + for (X509Certificate certificate: certificates) { + String name = certificate.getSubjectDN().getName(); + System.out.println(name); + } + + } catch (CertificateException | FileNotFoundException e) { + e.printStackTrace(); + } + } } diff --git a/src/test/java/com/axway/ExternalInstanceDomainCertTest.java b/src/test/java/com/axway/ExternalInstanceDomainCertTest.java new file mode 100644 index 0000000..5457244 --- /dev/null +++ b/src/test/java/com/axway/ExternalInstanceDomainCertTest.java @@ -0,0 +1,55 @@ +package com.axway; + +import org.junit.Test; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.File; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +public class ExternalInstanceDomainCertTest { + + CertHelper certHelper = new CertHelper(); + ExternalInstanceDomainCert externalInstanceDomainCert = new ExternalInstanceDomainCert(); + + @Test + public void testCerts(){ + try { + PKCS12 pkcs12 = certHelper.parseP12(new File(ClassLoader.getSystemResource("topology.p12").getFile()), "".toCharArray()); + File certsXml = new File("src/"); + certsXml = new File(certsXml, "certs.xml"); + externalInstanceDomainCert.certsFile(pkcs12, certsXml); + } catch (KeyStoreException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (CertificateException e) { + e.printStackTrace(); + } catch (UnrecoverableKeyException e) { + e.printStackTrace(); + } + } + + @Test + public void testUpdateMgmtFile(){ + File file = new File("src/mgmt.xml"); + try { + externalInstanceDomainCert.updateMgmtFile(file, "cn=dss"); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (SAXException e) { + e.printStackTrace(); + } catch (TransformerException e) { + e.printStackTrace(); + } + } +} diff --git a/src/test/resources/certchain.pem b/src/test/resources/certchain.pem new file mode 100644 index 0000000..edf7298 --- /dev/null +++ b/src/test/resources/certchain.pem @@ -0,0 +1,39 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhICCQCBqBgKx63lZzANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQVoxEzARBgNVBAcMClNjb3R0c2RhbGUxDjAMBgNVBAoMBUFY +V0FZMRYwFAYDVQQDDA1DQUNFUlRJRklDQVRFMB4XDTIxMDEyODIzMzAxOFoXDTMx +MDEyNjIzMzAxOFowVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkFaMRMwEQYDVQQH +DApTY290dHNkYWxlMQ4wDAYDVQQKDAVBWFdBWTEWMBQGA1UEAwwNQ0FDRVJUSUZJ +Q0FURTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVg4lsT07yviqti +qfeT0DZ3kOUfDE2mRDRIlHFM0XnbZyKLFEih5vq+SSHJZQ3B6WX1JY/Ya+srpk0q +kDV6fU8VyO7auGw6v8hv2UYxIAak4ubgpD+KFlycArGqYFh9pxIepphlj96kaS8j +pyF7NcRiRoQs39BBx9Qv0sbsyQNBaDa7iJR+MO/tvEiKFjVmdmRKyLcLg4iGGUlj +QeL2UcHdXhqP2YynKrVYJ+XyFEi4vWQG3xjOzjQeKA5MSHAlYaHmsxtoJ8qIB0M+ ++e2qszPBwX1/0ZVTLK6rEccZmc79VPXgL+tLcwYIouPxI0TLkPZtP8c2i5LbPLM4 +VaQLemUCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAcV5Q4Pz8MDJq4uGFazlf2YhR +PQgcPwjx/snX4PiU4grAmxhkGSH7lNGXHzJChIqToqaCmQsy+r1dRHYvz+9jugdH +FRvb6LkFovsowSqX2s/b4a8O7xy4I2LUywmw5x18Qs+4E2xbUjK9etPjGcGh2A0c +c6lGXwr4zsgno9Ab3HJ697lfhIlsW04aKfbYyGHH+QZNSOBl9ntgYJPMEcyoMf1S +b97wugInnNFAIl4XuNgUEJ+120p4PQBFi32p4hL/vyjiyjGeNkLeYRrsjK7Q/2Vs +/UxLHkyi75+bBBjFEC/Ua3n8LvZVZSRaltUcbBGaAbZsJIw1IcAPZLBuX0o8cQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDMTCCAhkCCQC6WubUQRFOnTANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQVoxEzARBgNVBAcMClNjb3R0c2RhbGUxDjAMBgNVBAoMBUFY +V0FZMRYwFAYDVQQDDA1DQUNFUlRJRklDQVRFMB4XDTIxMDEyODIzMzAzMFoXDTIy +MDEyMzIzMzAzMFowXjEPMA0GA1UEAwwGZHNzYXBpMQwwCgYDVQQLDANEU1MxEzAR +BgNVBAcMClNjb3R0c2RhbGUxCzAJBgNVBAYTAlVTMQ4wDAYDVQQKDAVBeHdheTEL +MAkGA1UECAwCQVowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQvJuk +/SSvMBGbRdUNH1ZrZ8whjrhIMGIV80bY3sZHnqqC4eBo9IHn+7ABmEaVlsXMoBD4 +hr+kKnZzi/SXqBr40fcg4X0zZGAaOlpYISi7kVA43f7kyFEbfbnVyjkNBhDKdALV +JZwtT4xe7dVB9tfNQULvyf6osw42QUipfVn/awcVUt1VLXR8355XV+vy2m46UG+9 +ptHuyxvjn/bGqVSrG06KZCAGZVouR6UkeIYbLFA/Q8FWlduNr73g81APU+T+pfGa +YrF49xuC8j3mmJgNXscNQCJnKxELwhekouxbnc9wSUjgCdA0v3tg0nbxvP48u9f6 +gY/mgZn1dIL0+/4hAgMBAAEwDQYJKoZIhvcNAQELBQADggEBALkI3sRWSxZLf9Dl +Cg81DV+zlm7TmJg+PyGctRcAXZWWlhNZUHZC4RT9+SBu8uiw3kMngNrAHd41MGH6 +dRaQj8EWMmrlRlgMpv8QbXCTJE85HksojLS6GXGrV28/WREpOhAE/u5Ln7prTjTz +tCcz1Jm949OKi+ynIDyOC8QRAlJlBsbvnWcBZwGrOBtfgNnEqS29AVn9USCCoOdx +K1FGiXGm5kqxsltrge6PZuYzGgq3wnCUosIwNpnXygptdeurvFdSJmetUZLSlovR +K4EnKbG9NfBVCJwrcdowmthwEUU9ECDGV0QSVh3iRzk/VDCG8eVg6x5MtHIRpLJB +8AP/mic= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/src/test/resources/topology.p12 b/src/test/resources/topology.p12 new file mode 100644 index 0000000..a68ddb7 Binary files /dev/null and b/src/test/resources/topology.p12 differ