mirror of
https://github.com/sparrowwallet/signpackage.git
synced 2024-11-02 12:26:47 +00:00
Initial checkin
This commit is contained in:
commit
e8028654ff
3 changed files with 200 additions and 0 deletions
3
build.sh
Normal file
3
build.sh
Normal 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
72
pom.xml
Normal 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>
|
125
src/main/java/com/dgunia/signpackage/Main.kt
Normal file
125
src/main/java/com/dgunia/signpackage/Main.kt
Normal 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(" ")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue