Skip to content

Commit

Permalink
added ability to pass app.xml as --keystore parameter to the verify-p…
Browse files Browse the repository at this point in the history
…ackage command

in this case it will use the trusted-certificates attribute
  • Loading branch information
shannah committed Oct 4, 2024
1 parent 638c327 commit 48f4c5f
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private void validateParameters(Parameters params) {

protected KeyStore loadTrustedCertificates(String keyStore) throws Exception {
if (isPemString(keyStore)) {
return CertificateUtil.loadCertificatesFromPEM(keyStore);
return CertificateUtil.loadCertificatesFromPEM(keyStore, new CommonNameAliasProvider());
} else if (isJksFile(keyStore)) {
return loadJksFile(keyStore);
} else if (isPemFile(keyStore)) {
Expand All @@ -92,11 +92,17 @@ protected KeyStore loadTrustedCertificates(String keyStore) throws Exception {
return loadPkcs12File(keyStore);
} else if (isPkcs7File(keyStore)) {
return loadPkcs7File(keyStore);
} else if (isAppXml(keyStore)) {
return CertificateUtil.loadTrustedCertificatesFromAppXml(keyStore, new CommonNameAliasProvider());
} else {
throw new IllegalArgumentException("Invalid key store format");
}
}

private boolean isAppXml(String keyStore) {
return isFile(keyStore, "app.xml");
}

private boolean isPemString(String keyStore) {
return keyStore.startsWith("-----BEGIN CERTIFICATE-----");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ca.weblite.tools.security;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;

class AppXmlTrustedCertificatesExtractor {

public static String extractTrustedCertificates(String xmlFilePath) {
try {
// Initialize the DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);

// Create a DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();

// Parse the XML file and load it into a Document
Document document = builder.parse(new File(xmlFilePath));

// Get the root element (in this case, <app>)
Element rootElement = document.getDocumentElement();

// Get the value of the "trusted-certificates" attribute
String trustedCertificates = rootElement.getAttribute("trusted-certificates");

return trustedCertificates;

} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ca.weblite.tools.security;

import java.security.cert.X509Certificate;

public interface CertificateAliasProvider {
public String getCertificateAlias(X509Certificate certificate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,20 @@ public static byte[] getSHA256(byte[] input) throws NoSuchAlgorithmException
return md.digest(input);
}

public static KeyStore loadCertificatesFromPEM(String pemEncodedCertificates) throws Exception {
public static KeyStore loadTrustedCertificatesFromAppXml(
String appXmlPath,
CertificateAliasProvider aliasProvider
) throws Exception {
return loadCertificatesFromPEM(
AppXmlTrustedCertificatesExtractor.extractTrustedCertificates(appXmlPath),
aliasProvider
);
}

public static KeyStore loadCertificatesFromPEM(
String pemEncodedCertificates,
CertificateAliasProvider aliasProvider
) throws Exception {
// Create an empty KeyStore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null); // Initialize the KeyStore with null parameters
Expand All @@ -235,7 +248,11 @@ public static KeyStore loadCertificatesFromPEM(String pemEncodedCertificates) th
X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(decoded));

// Add the certificate to the KeyStore with a unique alias
keyStore.setCertificateEntry("cert-" + certIndex, certificate);
if (aliasProvider == null) {
keyStore.setCertificateEntry("cert-" + certIndex, certificate);
} else {
keyStore.setCertificateEntry(aliasProvider.getCertificateAlias(certificate), certificate);
}
certIndex++;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ca.weblite.tools.security;

import java.security.cert.X509Certificate;

public class CommonNameAliasProvider implements CertificateAliasProvider {
@Override
public String getCertificateAlias(X509Certificate certificate) {
String commonName = certificate.getSubjectX500Principal().getName();
if (commonName.startsWith("CN=")) {
return commonName.substring(3);
} else {
return commonName;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ca.weblite.tools.security;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

public class CertificateUtilTest {

private String tempXmlFilePath;
private X509Certificate certificate;
private String certificateName = "Test Certificate";

@BeforeEach
public void setUp() throws Exception {
// Generate a self-signed certificate
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
certificate = SelfSignedCertificateGenerator.generateSelfSignedCertificate(keyPair, certificateName);

// Encode the certificate to PEM format
String pemEncodedCert = CertificateUtil.toPemEncodedString(certificate);

// Create a temporary XML file with the trusted-certificates attribute
File tempXmlFile = File.createTempFile("app", ".xml");
tempXmlFilePath = tempXmlFile.getAbsolutePath();

// Embed the PEM-encoded certificate in the XML
String xmlContent = "<app trusted-certificates=\"" + pemEncodedCert + "\"/>";

try (FileWriter writer = new FileWriter(tempXmlFile)) {
writer.write(xmlContent);
}
}

@Test
public void testLoadTrustedCertificatesFromAppXml() throws Exception {
// Load the certificates from the app XML
KeyStore keyStore = CertificateUtil.loadTrustedCertificatesFromAppXml(tempXmlFilePath, new CommonNameAliasProvider());
// Extract the expected alias (common name) from the generated certificate
String expectedAlias = certificate.getSubjectX500Principal().getName().split("CN=")[1].split(",")[0];
// Ensure that the certificate is correctly loaded into the KeyStore
Certificate loadedCert = keyStore.getCertificate(expectedAlias);
assertNotNull(loadedCert, "The certificate should be present in the KeyStore");
assertEquals(certificate, loadedCert, "The loaded certificate should match the generated certificate");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ca.weblite.tools.security;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Date;


public class SelfSignedCertificateGenerator {

public static X509Certificate generateSelfSignedCertificate(KeyPair keyPair, String commonName) throws Exception {
long now = System.currentTimeMillis();
Date startDate = new Date(now);

X500Name dnName = new X500Name("CN=" + commonName);
BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // Using current time as the certificate serial number
Date endDate = new Date(now + 365 * 24 * 60 * 60 * 1000L); // 1 year validity

ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate());
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
dnName, certSerialNumber, startDate, endDate, dnName, keyPair.getPublic());

JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
return certConverter.getCertificate(certBuilder.build(contentSigner));
}
}

0 comments on commit 48f4c5f

Please sign in to comment.