diff --git a/.ci/docker/Dockerfile.aca-rocky b/.ci/docker/Dockerfile.aca-rocky new file mode 100644 index 00000000..a7806a4a --- /dev/null +++ b/.ci/docker/Dockerfile.aca-rocky @@ -0,0 +1,75 @@ +FROM rockylinux:9 +LABEL org.opencontainers.image.vendor NSA Laboratory for Advanced Cybersecurity Research +LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs +LABEL org.opencontainers.image.description NSA\'s HIRS Attestation Certificate Authority. Expose port 8443 to access the portal from outside the container. + +SHELL ["/bin/bash", "-c"] + +# Rocky 9 has a different channel for some apps +RUN dnf install -y 'dnf-command(config-manager)' && dnf config-manager --set-enabled crb + +# Update and install OS-dependencies +RUN dnf update -y +# Dependencies were selected for these reasons: +# OS setup/Unknown direct impact for HIRS +ENV HIRS_DNF_OS_SETUP="initscripts firewalld policycoreutils policycoreutils-python-utils net-tools" +# OS tools +ENV HIRS_DNF_OS_TOOLS="git sudo vim wget" +# ACA compile +ENV HIRS_DNF_ACA_COMPILE="java-17-openjdk-devel" +# ACA run +ENV HIRS_DNF_ACA_RUN="mariadb-server" +# IBM TPM simulator compile +ENV HIRS_DNF_TPM_COMPILE="tpm2-tools gcc cmake openssl-devel" +# Download and install all dependencies at one time +RUN dnf -y install $(echo "$HIRS_DNF_OS_SETUP") $(echo "$HIRS_DNF_OS_TOOLS") $(echo "$HIRS_DNF_ACA_COMPILE") $(echo "$HIRS_DNF_ACA_RUN") $(echo "$HIRS_DNF_TPM_COMPILE") + +# Set up TPM Simulator +RUN git clone https://github.com/kgoldman/ibmswtpm2 /ibmswtpm2 +WORKDIR /ibmswtpm2/src +RUN make +# The following script allows the TPM to be set up in the docker image. +# This will install an empty TPM. +RUN echo "#!/bin/bash" > /tmp/tpm_config && \ + echo "/ibmswtpm2/src/tpm_server &" >> /tmp/tpm_config && \ + echo "sleep 5" >> /tmp/tpm_config && \ + echo "tpm2_startup -c" >> /tmp/tpm_config && \ + bash /tmp/tpm_config && \ + rm -rf /tmp/tpm_config + +#EXPOSE 8080 # Only needed if TLS is not working. +EXPOSE 8443 + +# Checkout HIRS +RUN git clone -b main https://github.com/nsacyber/HIRS.git /repo + +# Defensive copy of the repo so it's easy to start fresh if needed +RUN mkdir /hirs +WORKDIR /repo +RUN cp -r . /hirs + +# Run bootwar to cache build artifacts +WORKDIR /hirs +RUN ./gradlew bootWar + +# Add ACA TLS certification path to container OS +# Allows the curl command in the HEALTHCHECK to work with TLS +# These commands are placed into a script that can be run after aca_setup.sh on container launch. +RUN echo "#!/bin/bash" > /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_intermediate_ca_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_intermediate_ca_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_root_ca_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_root_ca_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_leaf_ca3_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_leaf_ca3_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \ + echo "update-ca-trust" >> /tmp/hirs_add_aca_tls_path_to_os.sh +RUN chmod +x /tmp/hirs_add_aca_tls_path_to_os.sh + +# The container will report a health state based on when embedded tomcat finishes loading. If the ACA isn't loaded after the timeout, the container will report that it is unhealthy. +HEALTHCHECK --start-period=50s --interval=1s --timeout=90s CMD curl -f https://localhost:8443/HIRS_AttestationCAPortal/portal/index + +# Reset working directory +WORKDIR /hirs + +# On container launch, the database will be set up. Then bootRun should utilize build artifacts stored in the image. +CMD ["bash", "-c", "/hirs/package/scripts/aca/aca_setup.sh --unattended && /tmp/hirs_add_aca_tls_path_to_os.sh && /hirs/package/scripts/aca/aca_bootRun.sh"] \ No newline at end of file diff --git a/.ci/docker/Dockerfile.aca-windows b/.ci/docker/Dockerfile.aca-windows new file mode 100644 index 00000000..d8e5ec86 --- /dev/null +++ b/.ci/docker/Dockerfile.aca-windows @@ -0,0 +1,80 @@ +FROM mcr.microsoft.com/powershell:latest +LABEL org.opencontainers.image.vendor NSA Laboratory for Advanced Cybersecurity Research +LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs +LABEL org.opencontainers.image.description NSA\'s HIRS Attestation Certificate Authority in a Windows-native image. Expose port 8443 to access the portal from outside the container. + +SHELL ["pwsh", "-Command"] + +# Output Powershell Version +# This Dockerfile requires Powershell 7+. +RUN $PSVersionTable + +# Set up logging area +RUN mkdir -p C:/ProgramData/hirs/aca +RUN mkdir -p C:/ProgramData/hirs/log + +# Download and install Java 17 +RUN ((New-Object System.Net.WebClient).DownloadFile('https://download.oracle.com/java/17/archive/jdk-17.0.8_windows-x64_bin.exe', 'C:/jdk-17.0.8_windows-x64_bin.exe')) +RUN Write-Host "Installing JDK..." +RUN ./jdk-17.0.8_windows-x64_bin.exe /s +RUN Write-Host "Finished installing JDK." + +RUN ls 'C:\Program Files\Java' +RUN ls 'C:\Program Files\Java\jdk-17\' + +# Download and install Mariadb as a service +RUN ((New-Object System.Net.WebClient).DownloadFile('https://ftp.osuosl.org/pub/mariadb/mariadb-11.1.2/winx64-packages/mariadb-11.1.2-winx64.msi', 'C:/mariadb-11.1.2-winx64.msi')) +RUN Write-Host "Installing MariaDB..." +# mariadb silent install options https://mariadb.com/kb/en/installing-mariadb-msi-packages-on-windows/ +RUN Start-Process -Wait -FilePath msiexec.exe -ArgumentList @('/i', 'C:\mariadb-11.1.2-winx64.msi', 'ADDLOCAL=ALL', 'REMOVE=HeidiSQL', 'SERVICENAME=MariaDB', '/qn', '/L*V', 'C:/ProgramData/hirs/log/mariadb_install.log') +RUN Write-Host "Finished installing mariadb." + +RUN ls 'C:\Program Files' +RUN ls 'C:\Program Files\MariaDB 11.1' + +# Download and install Git +RUN ((New-Object System.Net.WebClient).DownloadFile('https://github.com/git-for-windows/git/releases/download/v2.42.0.windows.2/Git-2.42.0.2-64-bit.exe', 'C:/Git-2.42.0.2-64-bit.exe')) +RUN Write-Host "Installing Git..." +RUN Start-Process -FilePath 'C:/Git-2.42.0.2-64-bit.exe' -ArgumentList \"/VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /o:PathOption=CmdTools /o:BashTerminalOption=ConHost /o:EnableSymlinks=Enabled /COMPONENTS=gitlfs\" -Wait -PassThru +# Disable GCM machine-wide +RUN [Environment]::SetEnvironmentVariable('GCM_INTERACTIVE', 'Never', [System.EnvironmentVariableTarget]::Machine) +RUN Write-Host "Finished installing Git." + +# Expose ACA Port +EXPOSE 8443 8080 + +# Set Environment Variables +RUN setx JAVA_HOME 'C:\Program Files\Java\jdk-17' +RUN setx GIT_HOME 'C:\Program Files\Git' +RUN setx PATH '%JAVA_HOME%\bin;C:\Program Files\MariaDB 11.1\bin;%GIT_HOME%\bin;%PATH%' + +# Echo System Variables +RUN echo $Env:PATH +RUN echo $Env:GIT_HOME +RUN echo $Env:JAVA_HOME + +# Clone HIRS main +WORKDIR C:/ +RUN git config --global --add core.autocrlf false +RUN git config --global --add safe.directory '*' +RUN git clone -b v3_windows-package-scripts https://github.com/nsacyber/hirs.git C:/repo + +# Defensive copy of the repo so it's easy to start fresh if needed +WORKDIR C:/repo +RUN cp -Recurse -Force C:/repo C:/hirs + +# Ensure Windows configuration files are in place before build. +WORKDIR C:/hirs +RUN pwsh -Command pwsh -ExecutionPolicy Bypass ./package/win/aca/aca_win_config.ps1 + +# Run bootWar to cache build objects and dependencies +WORKDIR C:/hirs +RUN setx GRADLE_OPTS '-Dorg.gradle.daemon=false' +RUN ./gradlew.bat clean bootWar 2>&1 | tee -a C:/ProgramData/hirs/log/hirs_build.log +RUN cp ./HIRS_AttestationCAPortal/src/main/resources/application.win.properties C:/ProgramData/hirs/aca/ + +# Run ACA Setup- PKI and DB +WORKDIR C:/hirs +RUN pwsh -Command pwsh -ExecutionPolicy Bypass ./package/win/aca/aca_setup.ps1 -unattended + +CMD ["pwsh", "-Command", "pwsh -ExecutionPolicy Bypass C:/hirs/package/win/aca/aca_bootRun.ps1"] \ No newline at end of file diff --git a/.ci/docker/compose-acceptance-test.yml b/.ci/docker/compose-acceptance-test.yml new file mode 100644 index 00000000..378e4e30 --- /dev/null +++ b/.ci/docker/compose-acceptance-test.yml @@ -0,0 +1,31 @@ +version: "3.9" +services: + aca: # policy settings not saved, will have a clean database/default policy on each boot for now + image: ghcr.io/nsacyber/hirs/aca:alpha2 + container_name: aca + ports: + - "8443:8443" + networks: + hat_network: + ipv4_address: 172.16.1.75 + hat: + image: ghcr.io/nsacyber/hirs/hat:alpha4 + container_name: hat + ports: + - 53:53/tcp + - 53:53/udp + - 67:67/udp + - 68:68/udp + - 69:69 + - 80:80 + networks: + hat_network: + ipv4_address: 172.16.1.3 +networks: + hat_network: + driver: transparent + name: hat_network + ipam: + config: + - subnet: 172.16.1.0/24 + gateway: 172.16.1.1 \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java index 18bb0458..aa41407e 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java @@ -14,6 +14,7 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.PropertySources; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -28,8 +29,14 @@ import java.security.cert.X509Certificate; * Restful implementation of the {@link AttestationCertificateAuthority}. * Exposes the ACA methods as REST endpoints. */ -@PropertySource(value = "file:/etc/hirs/aca/application.properties", - ignoreResourceNotFound = true) +@PropertySources({ + // detects if file exists, if not, ignore errors + @PropertySource(value = "file:/etc/hirs/aca/application.properties", + ignoreResourceNotFound = true), + + @PropertySource(value = "file:C:/ProgramData/hirs/aca/application.win.properties", + ignoreResourceNotFound = true) +}) @RestController @RequestMapping("/HIRS_AttestationCA") public class RestfulAttestationCertificateAuthority extends AttestationCertificateAuthority implements RestfulInterface { diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java index 1bbac7ff..f969f751 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java @@ -53,6 +53,10 @@ import java.util.Properties; // detects if file exists, if not, ignore errors @PropertySource(value = "file:/etc/hirs/aca/aca.properties", + ignoreResourceNotFound = true), + @PropertySource(value = "file:/etc/hirs/aca/application.properties", + ignoreResourceNotFound = true), + @PropertySource(value = "file:C:/ProgramData/hirs/aca/application.win.properties", ignoreResourceNotFound = true) }) @ComponentScan({"hirs.attestationca.portal", "hirs.attestationca.portal.page.controllers", "hirs.attestationca.persist", "hirs.attestationca.persist.entity", "hirs.attestationca.persist.service"}) diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.win.properties b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties new file mode 100644 index 00000000..4fea8333 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties @@ -0,0 +1,52 @@ + +# Logging Config (tomcat may have further config) +logging.level.org.springframework=TRACE +logging.level.org.apache.catalina=TRACE +logging.level.org.springframework.web=TRACE +logging.level.org.hibernate=ERROR +logging.file.path=C:/ProgramData/hirs/log +logging.file.name=hirs.spring.log +# Database Config +spring.jpa.hibernate.ddl-auto=update +jakarta.persistence.sharedCache.mode = UNSPECIFIED +spring.datasource.driver-class-name=org.mariadb.jdbc.Driver +#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +#spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver +aca.certificates.validity = 3652 +# Tomcat Config +server.tomcat.additional-tld-skip-patterns=jakarta.persistence-api*.jar, jakarta.xml.bind-api*.jar, txw2*.jar, *commons*.jar, *annotations*.jar, *checker*.jar, *lombok*.jar, *jsr*.jar, *guava*.jar, *access*.jar, *activation*.jar, *bcprov*.jar, *bcmail*.jar, *bcutil*.jar, *bcpkix*.jar, *json*.jar +server.tomcat.basedir=C:/ProgramData/hirs/embeddedtomcat +server.servlet.register-default-servlet=true +server.servlet.context-path=/ +spring.mvc.servlet.path=/ + +server.tomcat.accesslog.enabled=true +server.tomcat.accesslog.directory=C:/ProgramData/hirs/log +server.tomcat.accesslog.file-date-format=yyyy-MM-dd +server.tomcat.accesslog.prefix=Tomcat_accesslog_ +server.tomcat.accesslog.suffix=.log +server.tomcat.accesslog.rotate=true + +# Tomcat TLS support +server.port=8443 +server.ssl.enabled=true +server.ssl.trust-store-type=JKS +server.ssl.trust-store=C:/ProgramData/hirs/certificates/HIRS/TrustStore.jks +server.ssl.trust-alias=hirs_aca_tls_rsa_3k_sha384 +server.ssl.key-store-type=JKS +server.ssl.key-store=C:/ProgramData/hirs/certificates/HIRS/KeyStore.jks +server.ssl.key-alias=hirs_aca_tls_rsa_3k_sha384 + +#--server.ssl.key-store-password=123456 +#--server.ssl.trust-store-password=123456 + +#jdbc.driverClassName = com.mysql.cj.jdbc.Driver +#jdbc.url = jdbc:mysql://localhost:3306/hirs_db?autoReconnect=true&useSSL=false +#jdbc.username = root +#jdbc.password = hirspass +#entitymanager.packagesToScan: hirs.attestationca.portal.page.controllers +#spring.jpa.hibernate.ddl-auto=update +#spring.jpa.show-sql=true + +# DB dfault password. +#spring.datasource.password=hirs_db diff --git a/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.win.xml b/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.win.xml new file mode 100644 index 00000000..16b13bf3 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.win.xml @@ -0,0 +1,35 @@ + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/resources/portal.properties.win b/HIRS_AttestationCAPortal/src/main/resources/portal.properties.win new file mode 100644 index 00000000..42196707 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/resources/portal.properties.win @@ -0,0 +1,37 @@ +# General notes: +# Properties are processed using an expression processor. That said, properties can inherit the +# values of other properties. +# +# In example the processor will resolve the value of aca.both as 'hello world!'. +# aca.hello = hello +# aca.world = world! +# aca.both = ${aca.hello} ${aca.world} +# +# ACA Directories +# root: the root directory of ACA related files +# certificates: the directory for ACA certificate files +aca.directories.root = C:/ProgramData/hirs/aca +aca.directories.certificates = ${aca.directories.root}/certificates + +# ACA certificate related properties. These are generic properties that apply to the creation of +# any certificate that the ACA is responsible for creating. +# validity: the number of days that credentials generated by the ACA are valid. +aca.certificates.validity = 3652 + +# ACA key store properties +# alias: the alias to reference the ACA key and certificate by +# location: the absolute path to the ACA key store. +# password: key store password +aca.keyStore.alias = HIRS_ACA_KEY +aca.keyStore.location = ${aca.directories.certificates}/keyStore.jks +aca.keyStore.password = + +# ACA setup/initialization properties. These properties are used exclusively by the ACA +# initialization process. Generally these properties do not need to be modified +# +# keySize: the default key size of the ACA key pair stored within the trust store +# subjectName: the CN of the generate X509 certificate +# expiration: the number of days that the generated X509 certificate will expire +aca.setup.keyStore.keySize = 2048 +aca.setup.keyStore.subjectName = HIRS_AttestationCA_Endorsement +aca.setup.keyStore.expiration = ${aca.certificates.validity} \ No newline at end of file diff --git a/package/win/aca/aca_bootRun.ps1 b/package/win/aca/aca_bootRun.ps1 new file mode 100644 index 00000000..5cac94bb --- /dev/null +++ b/package/win/aca/aca_bootRun.ps1 @@ -0,0 +1,102 @@ +param ( + [string]$p, [string]$path = $null, + [switch]$w, [switch]$war = $false, + [switch]$h, [switch]$help = $false +) + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path $APP_HOME 'aca_common.ps1') +$ALG="RSA" # or "ECC" +$GRADLE_WRAPPER='./gradlew' +$DEPLOYED_WAR=$null + +# Load other scripts +. $ACA_COMMON_SCRIPT + +# Set up log +set_up_log + +# Read aca.properties +read_aca_properties $global:HIRS_DATA_ACA_PROPERTIES_FILE + +# Read spring application.properties +read_spring_properties $global:HIRS_DATA_SPRING_PROP_FILE + +echo "-----------------------------------------------------------" | WriteAndLog +echo ("Running with these arguments: "+($PSBoundParameters | Out-String)) | WriteAndLog + +# Parameter Consolidation +if ($p -and $path -and ($p -ne $path)) { + "-p and --path were given different paths. Use only one." | WriteAndLog + $help=$true +} +if ($p) { + $path = $p +} +$war = $w -or $war +$help = $h -or $help + +if(!(New-Object Security.Principal.WindowsPrincipal( + [Security.Principal.WindowsIdentity]::GetCurrent()) + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + echo "This script requires root. Please run as root" + exit 1 +} + +if ($help) { + echo " Setup script for the HIRS ACA" + echo " Syntax: powershell -ExecutionPolicy Bypass aca_bootRun.ps1 [-h|p|w|--path|--war]" + echo " options:" + echo " -p | --path Path to the HIRS_AttestationCAPortal.war file" + echo " -w | --war Use deployed war file" + echo " -h | --help Print this help" + exit 1 +} + +if ($path) { + $DEPLOYED_WAR = $path +} + +# if ($ALG -eq "RSA") { + # $CERT_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_rsa_3k_sha384_Cert_Chain.pem') + # $CLIENT_DB_P12=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_client_rsa_3k_sha384.p12') + # $ALIAS="hirs_aca_tls_rsa_3k_sha384" +# } elseif ($ALG -eq "ECC") { + # $CERT_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_ECC_PATH 'HIRS_ecc_512_sha384_Cert_Chain.pem') + # $CLIENT_DB_P12=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_ECC_PATH 'HIRS_db_client_ecc_512_sha384.p12') + # $ALIAS="hirs_aca_tls_ecc_512_sha384" +# } + +if (![System.IO.Directory]::Exists($global:HIRS_DATA_CERTIFICATES_HIRS_DIR)) { + echo "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR directory does not exist. Please run aca_setup\.ps1 and try again." + exit 1; +} + +if (!$DEPLOYED_WAR) { + if (![System.IO.File]::Exists($GRADLE_WRAPPER)) { + echo 'This script is expected to be run from the HIRS top level project directory. Exiting.' + exit 1; + } + $DEPLOYED_WAR='./HIRS_AttestationCAPortal/build/libs/HIRS_AttestationCAPortal.war' +} + +# Run the embedded tomcat server with Web TLS enabled and database client TLS enabled by overrding critical parameters +# Note "&" is a sub parameter continuation, space represents a new parameter. Spaces and quotes matter. +# hibernate.connection.url is used for the DB connector which established DB TLS connectivity +# server.ssl arguments support the embeded tomcats use of TLS for the ACA Portal + +#$CONNECTOR_PARAMS="--hibernate.connection.url=jdbc:mariadb://localhost:3306/hirs_db?autoReconnect=true&user="+$global:ACA_PROPERTIES.'hirs_db_username'+"&password="+$global:ACA_PROPERTIES.'hirs_db_password'+"&sslMode=VERIFY_CA&serverSslCert=$CERT_CHAIN&keyStoreType=PKCS12&keyStorePassword="+$global:ACA_PROPERTIES.'hirs_pki_password'+"&keyStore=$CLIENT_DB_P12" | ChangeBackslashToForwardSlash + +#$WEB_TLS_PARAMS="--server.ssl.key-store-password="+$global:ACA_PROPERTIES.'hirs_pki_password'+ +#" --server.ssl.trust-store-password="+$global:ACA_PROPERTIES.'hirs_pki_password' | ChangeBackslashToForwardSlash + +$SPRING_PROP_FILE_FORWARDSLASHES=($global:HIRS_DATA_SPRING_PROP_FILE | ChangeBackslashToForwardSlash) +#echo $CONNECTOR_PARAMS +#echo $WEB_TLS_PARAMS +if ($w -or $war) { + echo "Booting the ACA from a war file..." | WriteAndLog + java -jar $DEPLOYED_WAR --spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES +} else { + echo "Booting the ACA from local build..." | WriteAndLog + ./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES" +} diff --git a/package/win/aca/aca_common.ps1 b/package/win/aca/aca_common.ps1 new file mode 100644 index 00000000..533098fe --- /dev/null +++ b/package/win/aca/aca_common.ps1 @@ -0,0 +1,224 @@ +# Globally set options +# HIRS System directories, if installed via MSI +# C:\Program Files\hirs # Executables +# bin +# HIRS_AttestationCA_Portal.war +# scripts +# See HIRS Relative directories description below +# HIRS Data directories, installed by these scripts +# C:\ProgramData\hirs # Configuration Files, Logs +# aca +# certificates +# HIRS +# ecc_512_sha384_certs +# rsa_3k_sha384_certs +# json +# log +# Other files needed: +# C:/MariaDB 11.1/data/my.ini +# If mysql is installed somewhere else, update DB_CONF below. +$global:HIRS_SYS_HOME=(Join-Path $Env:ProgramFiles "hirs") +$global:HIRS_INSTALL_SCRIPTS_DIR=(Join-Path $Env:ProgramFiles "scripts") +$global:HIRS_INSTALL_SCRIPTS_DB_DIR=(Join-Path $Env:ProgramFiles "db") +$global:HIRS_DATA_DIR=(Join-Path $Env:ProgramData "hirs") +$global:HIRS_CONF_DIR=(Join-Path $global:HIRS_DATA_DIR "aca") +$global:HIRS_DATA_ACA_PROPERTIES_FILE=(Join-Path $global:HIRS_CONF_DIR 'aca.properties') +$global:HIRS_DATA_SPRING_PROP_FILE=(Join-Path $global:HIRS_CONF_DIR 'application.win.properties') +$global:HIRS_DATA_CERTIFICATES_DIR=(Join-Path $global:HIRS_DATA_DIR "certificates") +$global:HIRS_DATA_CERTIFICATES_HIRS_DIR=(Join-Path $global:HIRS_DATA_CERTIFICATES_DIR "HIRS") +$global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH=(Join-Path $HIRS_DATA_CERTIFICATES_HIRS_DIR "rsa_3k_sha384_certs") +$global:HIRS_DATA_CERTIFICATES_HIRS_ECC_PATH=(Join-Path $HIRS_DATA_CERTIFICATES_HIRS_DIR "ecc_512_sha384_certs") +$global:HIRS_DATA_LOG_DIR=(Join-Path $global:HIRS_DATA_DIR "log") +$global:HIRS_DATA_INSTALL_LOG_NAME=(Join-Path $global:HIRS_DATA_LOG_DIR ("hirs_aca_install_"+(Get-Date -Format "yyyy-MM-dd")+'.log')) +$global:HIRS_DATA_JSON_DIR=(Join-Path $global:HIRS_DATA_DIR "json") +# Db Configuration files +$global:DB_CONF=(Join-Path $Env:ProgramFiles 'MariaDB 11.1' 'data' 'my.ini') +# Default Server Side Certificates +$global:SSL_DB_SRV_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_rsa_3k_sha384_Cert_Chain.pem') +$global:SSL_DB_SRV_CERT=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_srv_rsa_3k_sha384.pem') +$global:SSL_DB_SRV_KEY=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_srv_rsa_3k_sha384.key') +# Default Client Side Certificates +$global:SSL_DB_CLIENT_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_rsa_3k_sha384_Cert_Chain.pem') +$global:SSL_DB_CLIENT_CERT=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_client_rsa_3k_sha384.pem') +$global:SSL_DB_CLIENT_KEY=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_client_rsa_3k_sha384.key') +# HIRS Relative directories assumed structure +# package +# scripts +# aca +# db +# db_create.sql +# secure_mysql.sql +# pki +# ca.conf +# win +# aca +# aca_bootRun.ps1 +# aca_common.ps1 # This script. You are here. +# aca_setup.ps1 +# aca_win_config.ps1 +# db +# db_create.ps1 +# mysql_util.ps1 +# pki +# pki_chain_gen.ps1 +# pki_setup.ps1 +$global:HIRS_REL_WIN_ACA_HOME=(Split-Path -parent $PSCommandPath) +$global:HIRS_REL_WIN_HOME=(Join-Path -Resolve $global:HIRS_REL_WIN_ACA_HOME ..) +$global:HIRS_REL_PACKAGE_HOME=(Join-Path -Resolve $global:HIRS_REL_WIN_HOME ..) +$global:HIRS_REL_SCRIPTS_HOME=(Join-Path -Resolve $global:HIRS_REL_PACKAGE_HOME 'scripts') +$global:HIRS_REL_SCRIPTS_ACA_HOME=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_HOME 'aca') +$global:HIRS_REL_SCRIPTS_DB_HOME=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_HOME 'db') +$global:HIRS_REL_SCRIPTS_DB_CREATE_SQL=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_DB_HOME 'db_create.sql') +$global:HIRS_REL_SCRIPTS_DB_SECURE_MYSQL_SQL=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_DB_HOME 'secure_mysql.sql') +$global:HIRS_REL_SCRIPTS_PKI_HOME=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_HOME 'pki') +$global:HIRS_REL_SCRIPTS_PKI_CA_CONF=(Join-Path -Resolve $global:HIRS_REL_SCRIPTS_PKI_HOME 'ca.conf') +$global:HIRS_REL_WIN_ACA_BOOTRUN=(Join-Path -Resolve $global:HIRS_REL_WIN_ACA_HOME 'aca_bootRun.ps1') +$global:HIRS_REL_WIN_ACA_COMMON=(Join-Path -Resolve $global:HIRS_REL_WIN_ACA_HOME 'aca_common.ps1') +$global:HIRS_REL_WIN_ACA_SETUP=(Join-Path -Resolve $global:HIRS_REL_WIN_ACA_HOME 'aca_setup.ps1') +$global:HIRS_REL_WIN_ACA_SETUP=(Join-Path -Resolve $global:HIRS_REL_WIN_ACA_HOME 'aca_win_config.ps1') +$global:HIRS_REL_WIN_DB_HOME=(Join-Path -Resolve $global:HIRS_REL_WIN_HOME 'db') +$global:HIRS_REL_WIN_DB_CREATE=(Join-Path -Resolve $global:HIRS_REL_WIN_DB_HOME 'db_create.ps1') +$global:HIRS_REL_WIN_DB_MYSQL_UTIL=(Join-Path -Resolve $global:HIRS_REL_WIN_DB_HOME 'mysql_util.ps1') +$global:HIRS_REL_WIN_PKI_HOME=(Join-Path -Resolve $global:HIRS_REL_WIN_HOME 'pki') +$global:HIRS_REL_WIN_PKI_CHAIN_GEN=(Join-Path -Resolve $global:HIRS_REL_WIN_PKI_HOME 'pki_chain_gen.ps1') +$global:HIRS_REL_WIN_PKI_SETUP=(Join-Path -Resolve $global:HIRS_REL_WIN_PKI_HOME 'pki_setup.ps1') + +# Saved values +# $Env:HIRS_MYSQL_ROOT_PWD +# $Env:HIRS_PKI_PWD +$global:ACA_PROPERTIES=$null +$global:SPRING_PROPERTIES=$null + +# Common utility functions +Function read_aca_properties () { + # This converts the ACA properties file into a hash table + # Values are accessed by key like this: $propertyValue=$global:ACA_PROPERTIES.'example.property.key' + param ( + [string]$file = $null + ) + if (!$global:ACA_PROPERTIES -and $file -and [System.IO.File]::Exists($file)) { + $file_content=(Get-Content $file -Raw) + if ($file_content) { # File is not empty + # $file_content=([Regex]::Escape($file_content) -replace "(\\r)?\\n",[Environment]::NewLine) + # $global:ACA_PROPERTIES=(ConvertFrom-StringData($file_content)) + $global:ACA_PROPERTIES=(Get-Content -Path $file -Raw | ConvertFrom-StringData) + } else { # File is empty + # Initialize empty hash table + $global:ACA_PROPERTIES=@{} + } + } elseif ($file -and ![System.IO.File]::Exists($file)) { + $msg="Warning: ACA properties file not found. The path provided was: $file" + if ($global:LOG_FILE) { + echo "$msg" | WriteAndLog + } else { + Write-Host "$msg" + } + } +} + +Function add_new_aca_property () { + param ( + [string]$file = $null, + [string]$newKeyAndValue = $null + ) + if ($global:ACA_PROPERTIES -and $file -and $newKeyAndValue -and [System.IO.File]::Exists($file)) { + $msg="Writing KeyValue pair to $file" + if ($global:LOG_FILE) { + echo "$msg" | WriteAndLog + } else { + Write-Host "$msg" + } + Write-Host "NOT LOGGED: KeyValue pair: $newKeyAndValue to file $file" + echo "$newKeyAndValue" >> $file + $global:ACA_PROPERTIES=$null + read_aca_properties $file + } +} + +Function read_spring_properties () { + # This converts the application properties file into a hash table + # Values are accessed by key like this: $propertyValue=$global:SPRING_PROPERTIES.'example.property.key' + param ( + [string]$file = $null + ) + if (!$global:SPRING_PROPERTIES -and $file -and [System.IO.File]::Exists($file)) { + $file_content=(Get-Content $file -Raw) + if ($file_content) { # File is not empty + #$file_content=([Regex]::Escape($file_content) -replace "(\\r)?\\n",[Environment]::NewLine) + #$global:SPRING_PROPERTIES=(ConvertFrom-StringData($file_content)) + $global:SPRING_PROPERTIES=(Get-Content -Path $file -Raw | ConvertFrom-StringData) + } else { # File is empty + # Initialize empty hash table + $global:SPRING_PROPERTIES=@{} + } + } elseif ($file -and ![System.IO.File]::Exists($file)) { + $msg="Warning: Spring properties file not found. The path provided was: $file" + if ($global:LOG_FILE) { + echo "$msg" | WriteAndLog + } else { + Write-Host "$msg" + } + } +} + +Function add_new_spring_property () { + param ( + [string]$file = $null, + [string]$newKeyAndValue = $null + ) + if ($global:SPRING_PROPERTIES -and $file -and $newKeyAndValue -and [System.IO.File]::Exists($file)) { + $msg="Writing KeyValue pair to $file" + if ($global:LOG_FILE) { + echo "$msg" | WriteAndLog + } else { + Write-Host "$msg" + } + Write-Host "NOT LOGGED: KeyValue pair: $newKeyAndValue to file $file" + echo "$newKeyAndValue" >> $file + $global:SPRING_PROPERTIES=$null + read_spring_properties $file + } +} + +Function create_random () { + return (1..100 | % { Get-Random } | sha512sum | tr -dc 'a-zA-Z0-9') +} + +Function set_up_log () { + if (![System.IO.Directory]::Exists($global:HIRS_DATA_LOG_DIR)) { + mkdir -p $global:HIRS_DATA_LOG_DIR 2>&1 > $null + } + $global:LOG_FILE=$global:HIRS_DATA_INSTALL_LOG_NAME + touch $global:LOG_FILE +} + +Function print_all_variables () { + # intended for debugging + # this will print all variables and their values in the current context + Get-Variable | Out-String +} + +Function WriteAndLog () { + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] + [string]$msg + ) + # EXPECTS set_up_log() to be run and $global:LOG_FILE to be defined + Write-Host "$msg" + "$msg" >> "$global:LOG_FILE" +} + +Function ChangeBackslashToForwardSlash () { + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] + [string]$msg + ) + echo ($msg -replace "\\","/") +} + +Function ChangeFileBackslashToForwardSlash () { + param( + [string]$file = $null + ) + (Get-Content $file) -replace "\\","/" | Set-Content $file +} diff --git a/package/win/aca/aca_setup.ps1 b/package/win/aca/aca_setup.ps1 new file mode 100644 index 00000000..f594bae0 --- /dev/null +++ b/package/win/aca/aca_setup.ps1 @@ -0,0 +1,97 @@ +param ( + [switch]$sd, [switch]${skip-db} = $false, + [switch]$sp, [switch]${skip-pki} = $false, + [switch]$u, [switch]$unattended = $false, + [switch]$h, [switch]$help = $false +) + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path $APP_HOME 'aca_common.ps1') +$COMP_JSON=(Join-Path $APP_HOME '..' '..' '..' 'HIRS_AttestationCA' 'src' 'main' 'resources' 'component-class.json') +$VENDOR_TABLE=(Join-Path $APP_HOME '..' '..' '..' 'HIRS_AttestationCA' 'src' 'main' 'resources' 'vendor-table.json') + +# Load other scripts +. $ACA_COMMON_SCRIPT + +# Set up log +set_up_log +echo "-----------------------------------------------------------" | WriteAndLog +echo "ACA setup log file is $global:LOG_FILE" | WriteAndLog +echo ("Running with these arguments: "+($PSBoundParameters | Out-String)) | WriteAndLog + +# Read aca.properties +mkdir -F -p $global:HIRS_CONF_DIR 2>&1 > $null +mkdir -F -p $global:HIRS_DATA_JSON_DIR 2>&1 > $null +mkdir -F -p $global:HIRS_DATA_LOG_DIR 2>&1 > $null +#cp $COMP_JSON $global:HIRS_JSON_DIR +#cp $VENDOR_TABLE $global:HIRS_JSON_DIR +touch $global:HIRS_DATA_ACA_PROPERTIES_FILE # create it, if it doesn't exist +read_aca_properties $global:HIRS_DATA_ACA_PROPERTIES_FILE + +# Read spring application.properties +touch $global:HIRS_DATA_SPRING_PROP_FILE # create it, if it doesn't exist +read_spring_properties $global:HIRS_DATA_SPRING_PROP_FILE + +# Parameter Consolidation +$skipdb=$sd -or ${skip-db} +$skippki=$sp -or ${skip-pki} +$unattended=$u -or $unattended +$help = $h -or $help + +if ($help) { + echo " Setup script for the HIRS ACA" + echo " Syntax: sh aca_setup.sh [-u|h|sd|sp|--skip-db|--skip-pki]" + echo " options:" + echo " -u | --unattended Run unattended" + echo " -h | --help Print this Help." + echo " -sp | --skip-pki run the setup without pki setup." + echo " -sb | --skip-db run the setup without database setup." + exit 1 +} + +if(!(New-Object Security.Principal.WindowsPrincipal( + [Security.Principal.WindowsIdentity]::GetCurrent()) + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + echo "This script requires root. Please run as root" + exit 1 +} + + + +echo "HIRS ACA Setup initiated on $(date +%Y-%m-%d)" | WriteAndLog + +if (!$skippki) { + if (!$Env:HIRS_PKI_PWD) { + $HIRS_PKI_PWD=(create_random) + # NOTE: Writing to the environment variable did not work within the container + # This password will be stored in the ACA properties file. + echo "Using randomly generated password for the PKI key password" | WriteAndLog + Write-Host "NOT LOGGED: Using pki password=$HIRS_PKI_PWD" + } else { + $HIRS_PKI_PWD=$Env:HIRS_PKI_PWD + echo "Using system supplied password for the PKI key password" | WriteAndLog + } + pwsh -ExecutionPolicy Bypass $global:HIRS_REL_WIN_PKI_SETUP -LOG_FILE:"$global:LOG_FILE" -PKI_PASS:"$HIRS_PKI_PWD" -UNATTENDED:"$unattended" + if ($LastExitCode -eq 0) { + echo "ACA PKI setup complete" | WriteAndLog + } else { + echo "Error setting up ACA PKI" | WriteAndLog + exit 1 + } +} else { + echo ("ACA PKI setup not run due to presence of command line argument: "+($PSBoundParameters.Keys | grep -E "skip-pki|sp")) | WriteAndLog +} + +if (!$skipdb) { + pwsh -ExecutionPolicy Bypass $global:HIRS_REL_WIN_DB_CREATE -LOG_FILE:"$global:LOG_FILE" -UNATTENDED:"$unattended" + if ($LastExitCode -eq 0) { + echo "ACA database setup complete" | WriteAndLog + } else { + echo "Error setting up ACA DB" | WriteAndLog + exit 1 + } +} else { + echo ("ACA Database setup not run due to command line argument: "+($PSBoundParameters.Keys | grep -E "skip-db|sd")) | WriteAndLog +} + +echo "ACA setup complete" | WriteAndLog \ No newline at end of file diff --git a/package/win/aca/aca_win_config.ps1 b/package/win/aca/aca_win_config.ps1 new file mode 100644 index 00000000..f16dfddf --- /dev/null +++ b/package/win/aca/aca_win_config.ps1 @@ -0,0 +1,28 @@ +# This script is intended to only be run from within a cloned copy of the HIRS repository from GitHub. It is not meant to be deployed. +# This script swaps configuration files for Windows into place. + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path $APP_HOME 'aca_common.ps1') + +# Load other scripts +. $ACA_COMMON_SCRIPT + +# Set up paths to configuration files +$global:HIRS_REL_PORTAL_LOG4J_SPRING_XML=(Join-Path -Resolve $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources log4j2-spring.xml) +$global:HIRS_REL_PORTAL_APPLICATION_PROPERTIES=(Join-Path -Resolve $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources application.properties) +$global:HIRS_REL_PORTAL_LOG4J_SPRING_LINUX_XML=(Join-Path $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources log4j2-spring.linux.xml) +$global:HIRS_REL_PORTAL_APPLICATION_LINUX_PROPERTIES=(Join-Path $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources application.linux.properties) +$global:HIRS_REL_PORTAL_LOG4J_SPRING_WIN_XML=(Join-Path -Resolve $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources log4j2-spring.win.xml) +$global:HIRS_REL_PORTAL_APPLICATION_WIN_PROPERTIES=(Join-Path -Resolve $global:HIRS_REL_PACKAGE_HOME .. HIRS_AttestationCAPortal src main resources application.win.properties) +$global:HIRS_REL_WIN_PKI_CA_CONF=(Join-Path $global:HIRS_REL_WIN_PKI_HOME 'ca.conf') + +# Back up linux configuration files +mv $global:HIRS_REL_PORTAL_LOG4J_SPRING_XML $global:HIRS_REL_PORTAL_LOG4J_SPRING_LINUX_XML +mv $global:HIRS_REL_PORTAL_APPLICATION_PROPERTIES $global:HIRS_REL_PORTAL_APPLICATION_LINUX_PROPERTIES + +# Copy windows configuration files in place +cp $global:HIRS_REL_PORTAL_LOG4J_SPRING_WIN_XML $global:HIRS_REL_PORTAL_LOG4J_SPRING_XML +cp $global:HIRS_REL_PORTAL_APPLICATION_WIN_PROPERTIES $global:HIRS_REL_PORTAL_APPLICATION_PROPERTIES + +# Make a copy of the ca.conf file local to the windows scripts +cp $global:HIRS_REL_SCRIPTS_PKI_CA_CONF $global:HIRS_REL_WIN_PKI_CA_CONF diff --git a/package/win/db/db_create.ps1 b/package/win/db/db_create.ps1 new file mode 100644 index 00000000..fff0239e --- /dev/null +++ b/package/win/db/db_create.ps1 @@ -0,0 +1,188 @@ +#!/bin/bash +# +############################################################################### +# HIRS DB creation +# Environment variables used: +# a. HIRS_MYSQL_ROOT_PWD: Set this variable if mysql root password is already set +# b. HIRS_DB_PWD: Set the pwd if default password to hirs_db user needs to be changed +################################################################################ + +param ( + [string]$LOG_FILE = $null, + [switch]$unattended = $false +) + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path "$APP_HOME" .. aca aca_common.ps1) + +# Load other scripts +. $ACA_COMMON_SCRIPT +. $global:HIRS_REL_WIN_DB_MYSQL_UTIL + +# Read aca.properties +read_aca_properties $global:HIRS_DATA_ACA_PROPERTIES_FILE + +# Read spring application.properties +read_spring_properties $global:HIRS_DATA_SPRING_PROP_FILE + +# Parameter check +if ($LOG_FILE) { + touch $LOG_FILE + $global:LOG_FILE=$LOG_FILE +} else { + set_up_log +} + +touch $global:HIRS_DATA_ACA_PROPERTIES_FILE +touch $global:DB_CONF + +# Make sure required paths exist +mkdir -F -p $global:HIRS_CONF_DIR 2>&1 > $null +mkdir -F -p $global:HIRS_DATA_LOG_DIR 2>&1 > $null + +Function check_mysql_root_pwd () { + # Check if DB root password needs to be obtainedS + $DB_ADMIN_PWD="" + if (!$Env:HIRS_MYSQL_ROOT_PWD) { + # Create a 32 character random password + echo "Using randomly generated password for the DB admin" | WriteAndLog + $DB_ADMIN_PWD=(create_random) + Write-Host "NOT LOGGED: DB Admin will be set to $DB_ADMIN_PWD, please make note for next mysql use." + # Check unattended flag set m if not then prompt user for permission ot store mysql root password + if (!$unattended) { + $confirm=Read-Host 'Do you wish to save this password to the aca.properties file?' + if (($confirm -eq "y") -or ($confirm -eq "yes")) { # case-insensitive + add_new_aca_property -file:"$global:HIRS_DATA_ACA_PROPERTIES_FILE" -newKeyAndValue:"mysql_admin_password=$DB_ADMIN_PWD" + echo "Mysql root password saved locally" | WriteAndLog + } else { + echo "Mysql root password not saved locally" | WriteAndLog + } + } else { # unattended install + add_new_aca_property -file:"$global:HIRS_DATA_ACA_PROPERTIES_FILE" -newKeyAndValue:"mysql_admin_password=$DB_ADMIN_PWD" + echo "Mysql root password has been saved locally." | WriteAndLog + } + mysqladmin --user=root password "$DB_ADMIN_PWD" + } else { + $DB_ADMIN_PWD=$Env:HIRS_MYSQL_ROOT_PWD + echo "Using system variable supplied password" | WriteAndLog + } + # Make sure root password is correct + + mysql -u root -p"$DB_ADMIN_PWD" -e 'quit' 2>&1 | WriteAndLog + if ($LastExitCode -eq 0) { + echo "Mysql root password verified" | WriteAndLog + } else { + echo "MYSQL root password was not the default, not supplied, or was incorrect" | WriteAndLog + echo " please set the HIRS_MYSQL_ROOT_PWD system variable and retry." | WriteAndLog + echo " ********** ACA Mysql setup aborted ********" | WriteAndLog + exit 1 + } + return $DB_ADMIN_PWD +} + +Function set_mysql_tls () { + # Check DB server setup. If ssl params dont exist then we need to add them. + if (!(Get-Content $global:DB_CONF | grep "ssl")) { + # Add TLS files to my.ini- Assumes [client] section at the end, and no [server] section + echo "Updating $global:DB_CONF with ssl parameters..." | WriteAndLog + echo "ssl_ca=$SSL_DB_CLIENT_CHAIN" >> $global:DB_CONF + echo "ssl_cert=$SSL_DB_CLIENT_CERT" >> $global:DB_CONF + echo "ssl_key=$SSL_DB_CLIENT_KEY" >> $global:DB_CONF + echo "[server]" >> $global:DB_CONF + echo "ssl_ca=$global:SSL_DB_SRV_CHAIN" >> $global:DB_CONF + echo "ssl_cert=$global:SSL_DB_SRV_CERT" >> $global:DB_CONF + echo "ssl_key=$global:SSL_DB_SRV_KEY" >> $global:DB_CONF + ChangeFileBackslashToForwardSlash $global:DB_CONF + } else { + echo "$global:DB_CONF contains existing entry for ssl, skipping..." | WriteAndLog + } +} + +# Process HIRS DB USER +Function set_hirs_db_pwd () { + param ( + [string]$DB_ADMIN_PWD = $null + ) + if (!$DB_ADMIN_PWD) { + echo "set_hirs_db_pwd was called without supplying a required variable" | WriteAndLog + } + + $HIRS_PASS="" + if ($Env:HIRS_DB_PWD) { + $HIRS_PASS=$Env:HIRS_DB_PWD + echo "Using hirs_db password found in the environment variable HIRS_DB_PWD" | WriteAndLog + } elseif ($global:ACA_PROPERTIES.'hirs_db_password') { + $HIRS_PASS=$global:ACA_PROPERTIES.'hirs_db_password' + echo "Using hirs_db password found in the ACA properties file $global:HIRS_DATA_ACA_PROPERTIES_FILE" | WriteAndLog + } else { + echo "Using randomly generated password for the DB key password" | WriteAndLog + $HIRS_PASS=(create_random) + add_new_aca_property -file:"$global:HIRS_DATA_ACA_PROPERTIES_FILE" -newKeyAndValue:"hirs_db_username=hirs_db" + add_new_aca_property -file:"$global:HIRS_DATA_ACA_PROPERTIES_FILE" -newKeyAndValue:"hirs_db_password=$HIRS_PASS" + echo "Stored hirs_db password in the ACA properties file $global:HIRS_DATA_ACA_PROPERTIES_FILE" | WriteAndLog + } + + $RESULT=(mysql -u root -p"$DB_ADMIN_PWD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')") + if ($RESULT -eq 1) { + echo "hirs-db user exists" | WriteAndLog + } + + return $HIRS_PASS +} + +# Create a hirs_db with client side TLS enabled +Function create_hirs_db_with_tls () { + param ( + [string]$DB_ADMIN_PWD = $null, + [string]$HIRS_PASS = $null + ) + if (!$DB_ADMIN_PWD) { + echo "create_hirs_db_with_tls: DB_ADMIN_PWD not provided and is required" | WriteAndLog + } + if (!$HIRS_PASS) { + echo "create_hirs_db_with_tls: HIRS_PASS not provided and is required" | WriteAndLog + } + + # Check if hirs_db not created and create it if it wasn't + mysqlshow -u root -p"$DB_ADMIN_PWD" | grep "hirs_db" 2>&1 > $null + if ($LastExitCode -eq 0) { + echo "hirs_db exists, skipping hirs_db create" | WriteAndLog + } else { + echo "Creating hirs_db database" | WriteAndLog + mysql -u root -p"$DB_ADMIN_PWD" -e "source $global:HIRS_REL_SCRIPTS_DB_CREATE_SQL" + mysql -u root -p"$DB_ADMIN_PWD" -e "source $global:HIRS_REL_SCRIPTS_DB_SECURE_MYSQL_SQL" + mysql -u root -p"$DB_ADMIN_PWD" -e "ALTER USER 'hirs_db'@'localhost' IDENTIFIED BY '$HIRS_PASS'; FLUSH PRIVILEGES;" + } +} + +Function create_hibernate_url () { + param ( + [string]$ALG = $null + ) + + if ($ALG -eq "RSA") { + $CERT_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_rsa_3k_sha384_Cert_Chain.pem') + $CLIENT_DB_P12=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_RSA_PATH 'HIRS_db_client_rsa_3k_sha384.p12') + $ALIAS="hirs_aca_tls_rsa_3k_sha384" + } elseif ($ALG -eq "ECC") { + $CERT_CHAIN=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_ECC_PATH 'HIRS_ecc_512_sha384_Cert_Chain.pem') + $CLIENT_DB_P12=(Join-Path $global:HIRS_DATA_CERTIFICATES_HIRS_ECC_PATH 'HIRS_db_client_ecc_512_sha384.p12') + $ALIAS="hirs_aca_tls_ecc_512_sha384" + } + + $CONNECTOR_URL="hibernate.connection.url=jdbc:mariadb://localhost:3306/hirs_db?autoReconnect=true&user="+$global:ACA_PROPERTIES.'hirs_db_username'+"&password="+$global:ACA_PROPERTIES.'hirs_db_password'+"&sslMode=VERIFY_CA&serverSslCert=$CERT_CHAIN&keyStoreType=PKCS12&keyStorePassword="+$global:ACA_PROPERTIES.'hirs_pki_password'+"&keyStore=$CLIENT_DB_P12" | ChangeBackslashToForwardSlash + + # Save connector information to the application properties file. + add_new_spring_property -file:"$global:HIRS_DATA_SPRING_PROP_FILE" -newKeyAndValue:"$CONNECTOR_URL" +} + +# HIRS ACA Mysqld processing ... +check_mariadb_install -p +check_for_container -p +set_mysql_tls +start_mysqlsd -p +$DB_ADMIN_PWD=check_mysql_root_pwd +$HIRS_PASS=set_hirs_db_pwd -DB_ADMIN_PWD:"$DB_ADMIN_PWD" +create_hirs_db_with_tls -DB_ADMIN_PWD:"$DB_ADMIN_PWD" -HIRS_PASS:"$HIRS_PASS" +create_hibernate_url -ALG:"RSA" +mysqld_reboot -p diff --git a/package/win/db/mysql_util.ps1 b/package/win/db/mysql_util.ps1 new file mode 100644 index 00000000..cb9a181c --- /dev/null +++ b/package/win/db/mysql_util.ps1 @@ -0,0 +1,109 @@ +Function check_for_container () { + param ( + [switch]$p, [switch]$PRINT_STATUS = $false + ) + $PRINT_STATUS = $p -or $PRINT_STATUS; + + if((Get-ItemProperty -path HKLM:System\CurrentControlSet\Control).ContainerType) { + $global:DOCKER_CONTAINER=$true + } else { + $global:DOCKER_CONTAINER=$false + } + if ($PRINT_STATUS) { + ("This {0} running in a container." -f ('is not', 'is')[$global:DOCKER_CONTAINER]) + } +} + +Function check_mariadb_install () { + param ( + [switch]$p, [switch]$PRINT_STATUS = $false + ) + $PRINT_STATUS = $p -or $PRINT_STATUS; + + if (Get-Command "mysql.exe" -ErrorAction SilentlyContinue) { + $global:MYSQL_INSTALLED=$true + if ($PRINT_STATUS) { + echo "mysql is installed" | WriteAndLog + } + } else { + $global:MYSQL_INSTALLED=$false + if ($PRINT_STATUS) { + echo "mysql is NOT been installed, aborting install" | WriteAndLog + } + exit 1; + } +} + +Function start_mysqlsd () { + param ( + [switch]$p, [switch]$PRINT_STATUS = $false + ) + $PRINT_STATUS = $p -or $PRINT_STATUS + + $DB_STATUS=(check_mysql $PRINT_STATUS) + + $service=(Get-Service MariaDB -ErrorAction SilentlyContinue) + # Check if mysql is already running, if not initialize + if(!$DB_STATUS -or ($DB_STATUS -and $DB_STATUS.HasExited)) { + if ($PRINT_STATUS) { + echo "Running the mariadb db installer..." | WriteAndLog + } + & mariadb-install-db.exe 2>&1 | WriteAndLog + if ($PRINT_STATUS) { + echo "Attempting to start mysql..." | WriteAndLog + } + if($service -and ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Stopped)) { + $service.Start() 2>&1 | WriteAndLog + } else { + Start-Process mysqld.exe -WindowStyle Hidden 2>&1 | WriteAndLog + } + $DB_STATUS=(check_mysql $PRINT_STATUS) + } +} + +Function check_mysql () { + param ( + [switch]$p, [switch]$PRINT_STATUS = $false + ) + $PRINT_STATUS = $p -or $PRINT_STATUS; + + $DB_STATUS=(Get-Process -Name mysqld -ErrorAction SilentlyContinue) + if ($PRINT_STATUS) { + ("The DB {0} running." -f ('is not', 'is')[$DB_STATUS]) | WriteAndLog + } + return $DB_STATUS; +} + +# Removed check_mysql_root: Looked redundant to db_create:check_mysql_root_pwd + +Function check_db_cleared () { + mysql -u root -e 'quit' &> $null + if ($LastExitCode -eq 0) { + echo " Empty root password verified" | WriteAndLog + } else { + echo " Mysql Root password is not empty" | WriteAndLog + } + + $HIRS_DB_USER_EXISTS=(mysql -uroot -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')") + if ($HIRS_DB_USER_EXISTS -eq 1) { + echo " hirs_db user exists" | WriteAndLog + } else { + echo " hirs_db user does not exist" | WriteAndLog + } +} + +Function mysqld_reboot () { + param ( + [switch]$p, [switch]$PRINT_STATUS = $false + ) + $PRINT_STATUS = $p -or $PRINT_STATUS; + if ($PRINT_STATUS) { + echo "Attempting to restart mysql..." | WriteAndLog + } + if($service) { + $service.Stop() 2>&1 >> "$global:LOG_FILE" + $service.Start() 2>&1 >> "$global:LOG_FILE" + } else { + Start-Process mysqld.exe -WindowStyle Hidden 2>&1 | WriteAndLog + } +} diff --git a/package/win/pki/pki_chain_gen.ps1 b/package/win/pki/pki_chain_gen.ps1 new file mode 100644 index 00000000..58c6fa8b --- /dev/null +++ b/package/win/pki/pki_chain_gen.ps1 @@ -0,0 +1,292 @@ +#!/bin/bash +# Script to generate a PKI Stack (Root, Intermediate, and LEAF CAs) and a Base RIM Signer +# creates a folder based upon the actor name and places certs under an algoithm specific folder (e.g. rsa_certs) +# PARAMS: +# 1. Actor name string (e.g. "Server Manufacturer") +# 2. Algorithm string (e.g. rsa or ecc) +# 3. Key Bit Size string (e.g. 2048) +# 4. Hash Algorithm string (e.g. sha256) +# 5. PKI password used to protect PKI keys and certs +# +# Examples: +# pki_chain_gen.sh "PC Manufacturer" rsa 2048 sha256 "password" +# pki_chain_gen.sh "DISK Manufacturer" ecc 256 sha512 "password" +# +# A KeyStore and Trust Store are created for by Java Applications. Both will use the supplied password. + +param ( + [string]$ACTOR = $null, + [string]$ASYM_ALG = $null, + [string]$ASYM_SIZE = $null, + [string]$HASH_ALG = $null, + [string]$PASS = $null, + [string]$LOG_FILE = $null +) + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path $APP_HOME '..' 'aca' 'aca_common.ps1') + +# Load other scripts +. $ACA_COMMON_SCRIPT + +# Read aca.properties +read_aca_properties $global:HIRS_DATA_ACA_PROPERTIES_FILE + +# Parameter check +if (!$ACTOR -or !$ASYM_ALG -or !$ASYM_SIZE -or !$HASH_ALG -or ($ACTOR -eq "-h") -or ($ACTOR -eq "--help")) { + echo "parameter missing to pki_chain_gen.sh, exiting pki setup" | WriteAndLog + exit 1; +} +if ($LOG_FILE) { + $global:LOG_FILE=$LOG_FILE +} else { + set_up_log +} + +$ACTOR_ALT=("$ACTOR" -replace " ","_") +$ROOT_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$ACTOR test root ca" +$INT_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$ACTOR test intermediate ca" +$LEAF_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$ACTOR test ca" +$SIGNER_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$ACTOR test signer" +$SERVER_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$ACTOR aca" +$TRUSTSTORE="$global:HIRS_DATA_CERTIFICATES_DIR\$ACTOR_ALT\TrustStore.jks" +$TRUSTSTORE_P12="$global:HIRS_DATA_CERTIFICATES_DIR\$ACTOR_ALT\TrustStore.p12" +$KEYSTORE="$global:HIRS_DATA_CERTIFICATES_DIR\$ACTOR_ALT\KeyStore.jks" + +if (($ASYM_ALG -ne "rsa") -and ($ASYM_ALG -ne "ecc")) { + echo "$ASYM_ALG is an unsupported assymetric algorithm, exiting pki setup" | WriteAndLog + exit 1; +} + +switch ($ASYM_SIZE) { + 256 { + $KSIZE="256" + $ECC_NAME="secp256k1" + Break + } + 384 { + $KSIZE="384" + $ECC_NAME="secp384r1" + Break + } + 512 { + $KSIZE="512" + $ECC_NAME="secp521r1" + Break + } + 2048 { + $KSIZE="2k" + Break + } + 3072 { + $KSIZE="3k" + Break + } + 4096 { + $KSIZE="4k" + Break + } + Default { + echo "$ASYM_SIZE is an unsupported key size, exiting pki setup" | WriteAndLog + exit 1 + } +} + +# Use algorithm and key size to create unique file paths and Distinguished names +$NAME="$ACTOR $ASYM_ALG $KSIZE $HASH_ALG" +$CERT_FOLDER="$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG"+"_certs" +$PKI_ROOT="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_"+"root_ca_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$PKI_INT="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_intermediate_ca_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$PKI_CA1="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_leaf_ca1_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$PKI_CA2="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_leaf_ca2_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$PKI_CA3="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_leaf_ca3_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$RIM_SIGNER="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_rim_signer_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$TLS_SERVER="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_aca_tls_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$DB_SERVER="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_db_srv_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$DB_CLIENT="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_db_client"+"_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG" +$TRUST_STORE_FILE="$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER\$ACTOR_ALT"+"_"+"$ASYM_ALG"+"_"+"$KSIZE"+"_"+"$HASH_ALG"+"_Cert_Chain.pem" + +$ROOT_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME test root ca" +$INT_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME test intermediate ca" +$LEAF_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME test ca" +$SIGNER_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME test signer" +$TLS_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME portal" +$DB_SRV_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME DB Server" +$DB_CLIENT_DN="/C=US/ST=MD/L=Columbia/O=$ACTOR/CN=$NAME DB Client" + +# Add check for existing folder and halt if it exists +if ([System.IO.Directory]::Exists("$ACTOR_ALT/$CERT_FOLDER")) { + echo "Folder for $CERT_FOLDER exists, exiting..." | WriteAndLog + exit 1 +} + +# Intialize sub folders +echo "Creating PKI for $ACTOR_ALT using $KSIZE $ASYM_ALG and $HASH_ALG..." | WriteAndLog + +mkdir -F -p "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR" 2>&1 > $null +mkdir -F -p "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\$CERT_FOLDER" 2>&1 > $null +mkdir -F -p "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\ca\certs" 2>&1 > $null +cp "$global:HIRS_DATA_CERTIFICATES_DIR\ca.conf" "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\" | WriteAndLog +touch "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\ca\db" +touch "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\openssl-san.cnf" +if (![System.IO.File]::Exists("$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\ca\serial.txt")) { + echo "01" > "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\ca\serial.txt" | WriteAndLog +} + +# Function to add Cert to Truststore and key to Keystore +Function add_to_stores () { + param ( + [string]$CERT_PATH = $null + ) + + $ALIAS=[System.IO.Path]::GetFileName($CERT_PATH) # Use filename without path as an alias + echo "$CERT_PATH key objects will use the alias `"$ALIAS`" in trust stores." | WriteAndLog + echo "Exporting key and certificate to PKCS12." | WriteAndLog + # Add the cert and key to the key store.. make a p12 file to import into te keystore + openssl pkcs12 -export -in "$CERT_PATH.pem" -inkey "$CERT_PATH.key" -out "tmpkey.p12" -passin "pass:$PASS" -macalg SHA256 -keypbe AES-256-CBC -certpbe AES-256-CBC -passout "pass:$PASS" 2>&1 | WriteAndLog + echo "Adding $ALIAS to the $KEYSTORE" | WriteAndLog + # Use the p12 file to import into a java keystore via keytool + keytool -importkeystore -srckeystore "tmpkey.p12" -destkeystore $KEYSTORE -srcstoretype pkcs12 -srcstorepass $PASS -deststoretype jks -deststorepass $PASS -noprompt -alias 1 -destalias "$ALIAS" 2>&1 | WriteAndLog + echo "Adding $ALIAS to the $TRUSTSTORE" | WriteAndLog + # Import the cert into a java trust store via keytool + keytool -import -keystore $TRUSTSTORE -storepass $PASS -file "$CERT_PATH.pem" -noprompt -alias "$ALIAS" 2>&1 | WriteAndLog + # Remove the temp p1 file. + echo "Cleaning up after storing $ALIAS" | WriteAndLog + rm "tmpkey.p12" +} + +# Function to create an Intermediate Key, CSR, and Certificate +# PARMS: +# 1. Cert Type String +# 2. Issuer Key File Name +# 3. Subject Distinguished Name String + +Function create_cert () { + param ( + [string]$CERT_PATH = $null, + [string]$ISSUER = $null, + [string]$SUBJ_DN = $null, + [string]$EXTENSION = $null + ) + + $ISSUER_KEY="$ISSUER.key" + $ISSUER_CERT="$ISSUER.pem" + $ALIAS=[System.IO.Path]::GetFileName($CERT_PATH) # Use filename without path as an alias + echo "Creating key pair and CSR with DN=`"$SUBJ_DN`"." | WriteAndLog + echo " Key pair will be saved at location $CERT_PATH\" | WriteAndLog + echo " Key will use $ASYM_ALG $ASYM_SIZE and HASH Alg $HASH_ALG" | WriteAndLog + echo " Certificate will be issued by $ISSUER_CERT and its key $ISSUER_KEY." | WriteAndLog + echo " Key objects will use the alias `"$ALIAS`" in trust stores." | WriteAndLog + + # Database doesnt support encypted key so create DB without passwords + if ("$SUBJ_DN" -match "DB") { + echo "This key is intended for use with the database." | WriteAndLog + if ("$ASYM_ALG" -eq "rsa") { + openssl genrsa -out "$CERT_PATH.key" "$ASYM_SIZE" 2>&1 | WriteAndLog + openssl req -new -key "$CERT_PATH.key" -out "$CERT_PATH.csr" -subj "$SUBJ_DN" 2>&1 | WriteAndLog + } else { + openssl ecparam -genkey -name "$ECC_NAME" -out "$CERT_PATH.key" 2>&1 | WriteAndLog + openssl req -new -key "$CERT_PATH.key" -out "$CERT_PATH.csr" -$HASH_ALG -subj "$SUBJ_DN" 2>&1 | WriteAndLog + } + } else { + if ("$ASYM_ALG" -eq "rsa") { + openssl req -newkey rsa:"$ASYM_SIZE" -keyout "$CERT_PATH.key" -out "$CERT_PATH.csr" -subj "$SUBJ_DN" -passout "pass:$PASS" 2>&1 | WriteAndLog + } else { + openssl genpkey -algorithm "EC" -pkeyopt ec_paramgen_curve:P-521 -aes256 --pass "pass:$PASS" -out "$CERT_PATH.key" 2>&1 | WriteAndLog + openssl req -new -key "$CERT_PATH.key" -passin "pass:$PASS" -out "$CERT_PATH.csr" -$HASH_ALG -subj "$SUBJ_DN" 2>&1 | WriteAndLog + } + } + + echo "Sending CSR to the CA." | WriteAndLog + pushd "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR" | WriteAndLog + openssl ca -config ca.conf -keyfile "$ISSUER_KEY" -md $HASH_ALG -cert "$ISSUER_CERT" -extensions "$EXTENSION" -out "$CERT_PATH.pem" -in "$CERT_PATH.csr" -passin "pass:$PASS" -batch -notext 2>&1 | WriteAndLog + popd | WriteAndLog + # Increment the cert serial number + $SERIAL=(Get-Content "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR\ca\serial.txt") + echo "Cert Serial Number = $SERIAL" | WriteAndLog + echo "Exporting key and certificate to PKCS12." | WriteAndLog + # Add the cert and key to the key store. make a p12 file to import into te keystore + openssl pkcs12 -export -in "$CERT_PATH.pem" -inkey "$CERT_PATH.key" -out "tmpkey.p12" -passin "pass:$PASS" -macalg SHA256 -keypbe AES-256-CBC -certpbe AES-256-CBC -passout "pass:$PASS" 2>&1 | WriteAndLog + echo "Adding $ALIAS to $KEYSTORE" | WriteAndLog + # Use the p12 file to import into a java keystore via keytool + keytool -importkeystore -srckeystore "tmpkey.p12" -destkeystore $KEYSTORE -srcstoretype pkcs12 -srcstorepass $PASS -deststoretype jks -deststorepass $PASS -noprompt -alias 1 -destalias "$ALIAS" 2>&1 | WriteAndLog + # Import the cert into a java trust store via keytool + echo "Adding $ALIAS to $TRUSTSTORE" | WriteAndLog + keytool -import -keystore $TRUSTSTORE -storepass $PASS -file "$CERT_PATH.pem" -noprompt -alias "$ALIAS" 2>&1 | WriteAndLog + echo "Cleaning up after storing $ALIAS" | WriteAndLog + # Remove the temp p12 file. + rm "tmpkey.p12"# remove csr file + rm "$CERT_PATH.csr" +} + +Function create_cert_chain () { + + # Create an intermediate CA, Sign with Root CA + create_cert "$PKI_INT" "$PKI_ROOT" "$INT_DN" "ca_extensions" + + # Create a Leaf CA (CA1), Sign with intermediate CA + create_cert "$PKI_CA1" "$PKI_INT" ("$LEAF_DN"+1) "ca_extensions" + + # Create a Leaf CA (CA2), Sign with intermediate CA + create_cert "$PKI_CA2" "$PKI_INT" ("$LEAF_DN"+2) "ca_extensions" + + # Create a Leaf CA (CA3), Sign with intermediate CA + create_cert "$PKI_CA3" "$PKI_INT" ("$LEAF_DN"+3) "ca_extensions" + + # Create a RIM Signer + create_cert "$RIM_SIGNER" "$PKI_CA2" "$SIGNER_DN" "signer_extensions" + + # Create a ACA Sever Cert for TLS use + create_cert "$TLS_SERVER" "$PKI_CA3" "$TLS_DN" "server_extensions" + + # Create a DB Sever Cert for TLS use + create_cert "$DB_SERVER" "$PKI_CA3" "$DB_SRV_DN" "server_extensions" + + # Create a ACA Sever Cert for TLS use + create_cert "$DB_CLIENT" "$PKI_CA3" "$DB_CLIENT_DN" "server_extensions" + + echo "Creating concatenated PEM file." | WriteAndLog + # Create Cert trust store by adding the Intermediate and root certs + cat "$PKI_CA1.pem", "$PKI_CA2.pem", "$PKI_CA3.pem", "$PKI_INT.pem", "$PKI_ROOT.pem" > "$TRUST_STORE_FILE" + + echo "Verifying the concatenated PEM file works." | WriteAndLog + # Checking signer cert using trust store... + openssl verify -CAfile "$TRUST_STORE_FILE" "$RIM_SIGNER.pem" | WriteAndLog + + echo "Creating a PKCS12 file for the database client." | WriteAndLog + # Make JKS files for the mysql DB connector. P12 first then JKS... + openssl pkcs12 -export -in "$DB_CLIENT.pem" -inkey "$DB_CLIENT.key" -macalg SHA256 -keypbe AES-256-CBC -certpbe AES-256-CBC -passin "pass:$PASS" -passout "pass:$PASS" -name "mysqlclientkey" -out "$DB_CLIENT.p12" 2>&1 | WriteAndLog + + echo "Converting the database client PKCS12 file to JKS." | WriteAndLog + keytool -importkeystore -srckeystore "$DB_CLIENT.p12" -srcstoretype PKCS12 -srcstorepass $PASS -destkeystore "$DB_CLIENT.jks" -deststoretype jks -deststorepass $PASS 2>&1 | WriteAndLog +} + +if ("$ASYM_ALG" -eq "rsa") { + # Create Root CA key pair and self signed cert + echo "Generating RSA Root CA ...." | WriteAndLog + openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -aes256 --pass "pass:$PASS" -out "$PKI_ROOT.key" 2>&1 | WriteAndLog + + # Create a self signed CA certificate + pushd "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR" | WriteAndLog + openssl req -new -config ca.conf -x509 -days 3650 -key "$PKI_ROOT.key" -subj "$ROOT_DN" -extensions ca_extensions -out "$PKI_ROOT.pem" -passin "pass:$PASS" 2>&1 | WriteAndLog + popd | WriteAndLog + # Add the CA root cert to the Trust and Key stores + add_to_stores "$PKI_ROOT" + # Create an intermediate CA, 2 Leaf CAs, and Signer Certs + create_cert_chain +} + +if ("$ASYM_ALG" -eq "ecc") { + # Create Root CA key pair and self signed cert + echo "Generating Ecc Root CA ...." | WriteAndLog + openssl genpkey -algorithm "EC" -pkeyopt ec_paramgen_curve:P-521 -aes256 --pass "pass:$PASS" -out "$PKI_ROOT.key" 2>&1 | WriteAndLog + + # Create a self signed CA certificate + pushd "$global:HIRS_DATA_CERTIFICATES_HIRS_DIR" | WriteAndLog + openssl req -new -config ca.conf -x509 -days 3650 -key "$PKI_ROOT.key" -subj "$ROOT_DN" -extensions ca_extensions -out "$PKI_ROOT.pem" -passin "pass:$PASS" 2>&1 | WriteAndLog + popd | WriteAndLog + # Add the CA root cert to the Trust and Key stores + add_to_stores "$PKI_ROOT" + # Create an intermediate CA, 2 Leaf CAs, and Signer Certs + create_cert_chain +} \ No newline at end of file diff --git a/package/win/pki/pki_setup.ps1 b/package/win/pki/pki_setup.ps1 new file mode 100644 index 00000000..7f59d565 --- /dev/null +++ b/package/win/pki/pki_setup.ps1 @@ -0,0 +1,78 @@ +#!/bin/bash +############################################################################################ +# Creates 2 Certificate Chains for the ACA: +# 1 RSA 3K SHA 384 +# 2 ECC 512 SHA 384 +# +############################################################################################ + +param ( + [string]$LOG_FILE = $null, + [string]$PKI_PASS = $null, + [switch]$UNATTENDED = $false +) + +$APP_HOME=(Split-Path -parent $PSCommandPath) +$ACA_COMMON_SCRIPT=(Join-Path "$APP_HOME" .. aca aca_common.ps1) + +# Load other scripts +. $ACA_COMMON_SCRIPT + +# Read aca.properties +read_aca_properties $global:HIRS_DATA_ACA_PROPERTIES_FILE + +# Read spring application.properties +read_spring_properties $global:HIRS_DATA_SPRING_PROP_FILE + +# Parameter check +if ($LOG_FILE) { + touch $LOG_FILE + $global:LOG_FILE=$LOG_FILE +} else { + set_up_log +} + +if (!$PKI_PASS) { + if ($Env:HIRS_PKI_PWD) { + $PKI_PASS=$Env:HIRS_PKI_PWD + } else { + $PKI_PASS=(create_random) + echo "Using randomly generated password for the PKI key password" | WriteAndLog + } +} + +mkdir -p $global:HIRS_CONF_DIR 2>&1 > $null +echo "APP_HOME is $APP_HOME" | WriteAndLog + +# Check for sudo or root user +if(!(New-Object Security.Principal.WindowsPrincipal( + [Security.Principal.WindowsIdentity]::GetCurrent()) + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + echo "This script requires root. Please run as root" | WriteAndLog + exit 1 +} + +# Create Cert Chains +if (![System.IO.Directory]::Exists($global:HIRS_DATA_CERTIFICATES_DIR)) { + if ([System.IO.Directory]::Exists($global:HIRS_REL_WIN_PKI_HOME)) { + $PKI_SETUP_DIR=$global:HIRS_REL_WIN_PKI_HOME + } else { + $PKI_SETUP_DIR=$APP_HOME + } + echo "PKI_SETUP_DIR is $PKI_SETUP_DIR" | WriteAndLog + + mkdir -F -p $global:HIRS_DATA_CERTIFICATES_DIR 2>&1 > $null + + cp $PKI_SETUP_DIR/ca.conf $global:HIRS_DATA_CERTIFICATES_DIR + pwsh -ExecutionPolicy Bypass $PKI_SETUP_DIR/pki_chain_gen.ps1 "HIRS" "rsa" "3072" "sha384" "$PKI_PASS" "$global:LOG_FILE" + pwsh -ExecutionPolicy Bypass $PKI_SETUP_DIR/pki_chain_gen.ps1 "HIRS" "ecc" "512" "sha384" "$PKI_PASS" "$global:LOG_FILE" + + # Save the password to the ACA properties file. + add_new_aca_property -file:"$global:HIRS_DATA_ACA_PROPERTIES_FILE" -newKeyAndValue:"hirs_pki_password=$PKI_PASS" + + # Save connector information to the application properties file. + add_new_spring_property -file:"$global:HIRS_DATA_SPRING_PROP_FILE" -newKeyAndValue:"server.ssl.key-store-password=$PKI_PASS" + add_new_spring_property -file:"$global:HIRS_DATA_SPRING_PROP_FILE" -newKeyAndValue:"server.ssl.trust-store-password=$PKI_PASS" +} else { + echo "$global:HIRS_DATA_CERTIFICATES_DIR exists, skipping" | WriteAndLog +}