Initial checkin

This commit is contained in:
dom 2020-02-26 11:43:38 +01:00
commit e8028654ff
3 changed files with 200 additions and 0 deletions

3
build.sh Normal file
View file

@ -0,0 +1,3 @@
#!/bin/sh
mvn clean compile assembly:single
cp target/SignPackage-1.0-jar-with-dependencies.jar ./SignPackage.jar

72
pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dgunia</groupId>
<artifactId>SignPackage</artifactId>
<version>1.0</version>
<properties>
<kotlin.version>1.3.61</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>zt-zip</artifactId>
<version>1.11</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.dgunia.signpackage.MainKt</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,125 @@
package com.dgunia.signpackage
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
import org.zeroturnaround.zip.ZipUtil
import java.io.File
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.zip.ZipFile
fun main(args: Array<String>) {
SignPackage(args).run()
}
class SignPackage(val args: Array<String>) {
val tmpDir = File("tmpdir${System.currentTimeMillis()}")
val threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
private val OPTION_DIR = "d"
private val OPTION_SIGN_KEY = "k"
private val OPTION_ENTITLEMENTS = "e"
private val OPTION_RUNTIME = "r"
private val OPTION_TIMESTAMP = "t"
private val OPTION_EXCLUDE = "x"
private var excludedFiles: Array<String> = emptyArray()
fun run() {
val options = Options()
options.addOption(Option.builder(OPTION_DIR).longOpt("dir").desc("Directory to scan recursively").hasArg().required().build())
options.addOption(Option.builder(OPTION_SIGN_KEY).longOpt("signing-key").desc("Key name (e.g. Developer ID Application: John Public (xxxxxxxxxxx))").hasArg().required().build())
options.addOption(Option.builder(OPTION_ENTITLEMENTS).longOpt("entitlements").desc("Entitlements file").hasArg().build())
options.addOption(Option.builder(OPTION_RUNTIME).longOpt("runtime").desc("Harden using runtime parameter").build())
options.addOption(Option.builder(OPTION_TIMESTAMP).longOpt("timestamp").desc("Set secure timestamp using timestamp parameter").build())
options.addOption(Option.builder(OPTION_EXCLUDE).longOpt("exclude").hasArgs().desc("Set secure timestamp using timestamp parameter").build())
val parser = DefaultParser()
try {
val cmd = parser.parse(options, args)
// Create a temporary directory
if (!tmpDir.mkdirs()) {
System.err.println("Could not create directory ${tmpDir.name}.")
return
}
tmpDir.deleteOnExit()
// Excluded files
if (cmd.hasOption(OPTION_EXCLUDE)) {
excludedFiles = cmd.getOptionValues(OPTION_EXCLUDE)
}
// Start scanning and signing the jar files
scanRecursive(File(cmd.getOptionValue(OPTION_DIR)), cmd)
threadPool.shutdown()
threadPool.awaitTermination(1, TimeUnit.DAYS)
} catch (e: Exception) {
e.printStackTrace()
} finally {
tmpDir.deleteRecursively()
}
}
/**
* Searches all files and subdirectories for files that have to be signed.
*/
private fun scanRecursive(dir: File, cmd: CommandLine) {
dir.listFiles()?.forEach { file ->
if (!excludedFiles.contains(file.path)) {
if (file.isDirectory) {
scanRecursive(file, cmd)
} else if (file.name.endsWith(".jar")) {
threadPool.submit {
ZipFile(file).entries().asSequence().forEach { zipEntry ->
if (zipEntry.name.endsWith(".dylib")) {
// Extract, sign and compress the dylib file.
println("${file.absolutePath}: ${zipEntry.name}")
val dylibFile = File(tmpDir, File(zipEntry.name).name)
ZipUtil.unpackEntry(file, zipEntry.name, dylibFile)
signFile(dylibFile, cmd)
ZipUtil.replaceEntry(file, zipEntry.name, dylibFile)
}
}
// Sign the jar file
println(file.absolutePath)
signFile(file, cmd)
}
} else if (file.name.endsWith(".dylib") || file.canExecute()) {
println(file.absolutePath)
threadPool.submit { signFile(file, cmd) }
}
}
}
}
/**
* Sign the file using codesign
*/
private fun signFile(dylibFile: File, cmd: CommandLine) {
val command = ArrayList<String>()
command.add("codesign")
if (cmd.hasOption(OPTION_TIMESTAMP)) command.add("--timestamp")
if (cmd.hasOption(OPTION_RUNTIME)) command.addAll(listOf("--options", "runtime"))
if (cmd.hasOption(OPTION_ENTITLEMENTS)) command.addAll(listOf("--entitlements", File(cmd.getOptionValue(OPTION_ENTITLEMENTS)).absolutePath))
command.addAll(listOf("--deep", "-vvv", "-f"))
command.add("--sign")
command.add(cmd.getOptionValue(OPTION_SIGN_KEY))
command.add(dylibFile.absolutePath)
println(command.joinToString(" "))
val resultCode = ProcessBuilder()
.directory(dylibFile.parentFile)
.inheritIO()
.command(command)
.start()
.waitFor()
if (resultCode != 0) {
throw Exception("Resultcode $resultCode when executing ${command.joinToString(" ")}")
}
}
}