mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 09:01:08 +03:00
[FEATURE] Автогенерация сертификата на лету при его отсутствии
This commit is contained in:
parent
585fb06151
commit
625fb75f82
7 changed files with 99 additions and 8 deletions
|
@ -47,7 +47,7 @@ public void init() {
|
||||||
tasks.add(new AdditionalFixesApplyTask(server));
|
tasks.add(new AdditionalFixesApplyTask(server));
|
||||||
if (!server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
|
if (!server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
|
||||||
if (server.config.launcher.compress) tasks.add(new CompressBuildTask(server));
|
if (server.config.launcher.compress) tasks.add(new CompressBuildTask(server));
|
||||||
if(server.config.sign.enabled) tasks.add(new SignJarTask(server.config.sign, server));
|
tasks.add(new SignJarTask(server.config.sign, server));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
package pro.gravit.launchserver.binary.tasks;
|
package pro.gravit.launchserver.binary.tasks;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder;
|
||||||
|
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||||
import org.bouncycastle.cms.CMSException;
|
import org.bouncycastle.cms.CMSException;
|
||||||
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||||
|
import org.bouncycastle.operator.ContentSigner;
|
||||||
import org.bouncycastle.operator.OperatorCreationException;
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.binary.SignerJar;
|
import pro.gravit.launchserver.binary.SignerJar;
|
||||||
import pro.gravit.launchserver.config.LaunchServerConfig;
|
import pro.gravit.launchserver.config.LaunchServerConfig;
|
||||||
|
@ -12,12 +21,23 @@
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.UnrecoverableKeyException;
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
@ -45,7 +65,12 @@ public Path process(Path inputFile) throws IOException {
|
||||||
return toRet;
|
return toRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException {
|
public void sign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException {
|
||||||
|
if(config.enabled) stdSign(config, inputFile, signedFile);
|
||||||
|
else autoSign(inputFile, signedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stdSign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException {
|
||||||
KeyStore c = SignHelper.getStore(new File(config.keyStore).toPath(), config.keyStorePass, config.keyStoreType);
|
KeyStore c = SignHelper.getStore(new File(config.keyStore).toPath(), config.keyStorePass, config.keyStoreType);
|
||||||
try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> SignJarTask.gen(config, c),
|
try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> SignJarTask.gen(config, c),
|
||||||
config.metaInfSfName, config.metaInfKeyName);
|
config.metaInfSfName, config.metaInfKeyName);
|
||||||
|
@ -64,6 +89,30 @@ public static void sign(LaunchServerConfig.JarSignerConf config, Path inputFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void autoSign(Path inputFile, Path signedFile) throws IOException {
|
||||||
|
try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> {
|
||||||
|
try {
|
||||||
|
return genCertificate(srv.config.projectName, srv.publicKey, srv.privateKey);
|
||||||
|
} catch (OperatorCreationException | CertificateException | CMSException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AUTOGEN.SF", "AUTOGEN.EC");
|
||||||
|
ZipInputStream input = new ZipInputStream(IOHelper.newInput(inputFile))) {
|
||||||
|
//input.getManifest().getMainAttributes().forEach((a, b) -> output.addManifestAttribute(a.toString(), b.toString())); // may not work such as after Radon.
|
||||||
|
ZipEntry e = input.getNextEntry();
|
||||||
|
while (e != null) {
|
||||||
|
if ("META-INF/MANIFEST.MF".equals(e.getName()) || "/META-INF/MANIFEST.MF".equals(e.getName())) {
|
||||||
|
Manifest m = new Manifest(input);
|
||||||
|
m.getMainAttributes().forEach((a, b) -> output.addManifestAttribute(a.toString(), b.toString()));
|
||||||
|
e = input.getNextEntry();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
output.addFileContents(IOHelper.newZipEntry(e), input);
|
||||||
|
e = input.getNextEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean allowDelete() {
|
public boolean allowDelete() {
|
||||||
|
@ -80,4 +129,26 @@ public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static CMSSignedDataGenerator genCertificate(String projectName, ECPublicKey publicKey, ECPrivateKey privateKey) throws OperatorCreationException, CertificateException, CMSException {
|
||||||
|
|
||||||
|
X500NameBuilder subject = new X500NameBuilder();
|
||||||
|
subject.addRDN(BCStyle.CN, projectName.concat(" Autogenerated"));
|
||||||
|
subject.addRDN(BCStyle.O, projectName);
|
||||||
|
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
||||||
|
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
|
||||||
|
subject.build(),
|
||||||
|
new BigInteger("0"),
|
||||||
|
Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant()),
|
||||||
|
Date.from(startDate.plusDays(3650).atZone(ZoneId.systemDefault()).toInstant()),
|
||||||
|
new X500Name("CN=ca"),
|
||||||
|
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
|
||||||
|
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256WITHECDSA");
|
||||||
|
ContentSigner signer = csBuilder.build(privateKey);
|
||||||
|
X509CertificateHolder certificate = builder.build(signer);
|
||||||
|
X509Certificate x509Certificate = new JcaX509CertificateConverter().setProvider( "BC" )
|
||||||
|
.getCertificate( certificate );
|
||||||
|
ArrayList<Certificate> chain = new ArrayList<>();
|
||||||
|
chain.add(x509Certificate);
|
||||||
|
return SignHelper.createSignedDataGenerator(privateKey, x509Certificate, chain, "SHA256WITHECDSA");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.binary.tasks.SignJarTask;
|
import pro.gravit.launchserver.binary.tasks.SignJarTask;
|
||||||
|
import pro.gravit.launchserver.binary.tasks.TaskUtil;
|
||||||
import pro.gravit.launchserver.command.Command;
|
import pro.gravit.launchserver.command.Command;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
@ -13,13 +14,18 @@
|
||||||
public class SignDirCommand extends Command {
|
public class SignDirCommand extends Command {
|
||||||
private class SignJarVisitor extends SimpleFileVisitor<Path>
|
private class SignJarVisitor extends SimpleFileVisitor<Path>
|
||||||
{
|
{
|
||||||
|
private SignJarTask task;
|
||||||
|
public SignJarVisitor(SignJarTask task) {
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
if (file.toFile().getName().endsWith(".jar"))
|
if (file.toFile().getName().endsWith(".jar"))
|
||||||
{
|
{
|
||||||
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
|
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
|
||||||
LogHelper.info("Signing jar %s", file.toString());
|
LogHelper.info("Signing jar %s", file.toString());
|
||||||
SignJarTask.sign(server.config.sign, file, tmpSign);
|
task.sign(server.config.sign, file, tmpSign);
|
||||||
Files.deleteIfExists(file);
|
Files.deleteIfExists(file);
|
||||||
Files.move(tmpSign, file);
|
Files.move(tmpSign, file);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +52,8 @@ public void invoke(String... args) throws Exception {
|
||||||
Path targetDir = Paths.get(args[0]);
|
Path targetDir = Paths.get(args[0]);
|
||||||
if(!IOHelper.isDir(targetDir))
|
if(!IOHelper.isDir(targetDir))
|
||||||
throw new IllegalArgumentException(String.format("%s not directory", targetDir.toString()));
|
throw new IllegalArgumentException(String.format("%s not directory", targetDir.toString()));
|
||||||
IOHelper.walk(targetDir, new SignJarVisitor(), true);
|
SignJarTask task = (SignJarTask) TaskUtil.getTaskByClass(server.launcherBinary.tasks, SignJarTask.class);
|
||||||
|
IOHelper.walk(targetDir, new SignJarVisitor(task), true);
|
||||||
LogHelper.info("Success signed");
|
LogHelper.info("Success signed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.binary.tasks.SignJarTask;
|
import pro.gravit.launchserver.binary.tasks.SignJarTask;
|
||||||
|
import pro.gravit.launchserver.binary.tasks.TaskUtil;
|
||||||
import pro.gravit.launchserver.command.Command;
|
import pro.gravit.launchserver.command.Command;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
|
@ -34,7 +35,8 @@ public void invoke(String... args) throws Exception {
|
||||||
else
|
else
|
||||||
tmpSign = server.dir.resolve("build").resolve(target.toFile().getName());
|
tmpSign = server.dir.resolve("build").resolve(target.toFile().getName());
|
||||||
LogHelper.info("Signing jar %s to %s", target.toString(), tmpSign.toString());
|
LogHelper.info("Signing jar %s to %s", target.toString(), tmpSign.toString());
|
||||||
SignJarTask.sign(server.config.sign, target, tmpSign);
|
SignJarTask task = (SignJarTask) TaskUtil.getTaskByClass(server.launcherBinary.tasks, SignJarTask.class);
|
||||||
|
task.sign(server.config.sign, target, tmpSign);
|
||||||
if(args.length <= 1)
|
if(args.length <= 1)
|
||||||
{
|
{
|
||||||
LogHelper.info("Move temp jar %s to %s", tmpSign.toString(), target.toString());
|
LogHelper.info("Move temp jar %s to %s", tmpSign.toString(), target.toString());
|
||||||
|
|
|
@ -227,7 +227,7 @@ public static class CertificateConf {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class JarSignerConf {
|
public static class JarSignerConf {
|
||||||
public boolean enabled = true;
|
public boolean enabled = false;
|
||||||
public String keyStore = "pathToKey";
|
public String keyStore = "pathToKey";
|
||||||
public String keyStoreType = "JKS";
|
public String keyStoreType = "JKS";
|
||||||
public String keyStorePass = "mypass";
|
public String keyStorePass = "mypass";
|
||||||
|
|
|
@ -144,6 +144,17 @@ public static CMSSignedDataGenerator createSignedDataGenerator(KeyStore keyStore
|
||||||
generator.addCertificates(certStore);
|
generator.addCertificates(certStore);
|
||||||
return generator;
|
return generator;
|
||||||
}
|
}
|
||||||
|
public static CMSSignedDataGenerator createSignedDataGenerator(PrivateKey privateKey, Certificate cert, List<Certificate> certChain, String signAlgo) throws OperatorCreationException, CertificateEncodingException, CMSException {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
Store certStore = new JcaCertStore(certChain);
|
||||||
|
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
|
||||||
|
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||||
|
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||||
|
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||||
|
generator.addSignerInfoGenerator(sig);
|
||||||
|
generator.addCertificates(certStore);
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
public static final String hashFunctionName = "SHA-256";
|
public static final String hashFunctionName = "SHA-256";
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ public X509CertificateHolder generateCertificate(String subjectName, PublicKey s
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorCreationException, InvalidAlgorithmParameterException {
|
public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorCreationException, InvalidAlgorithmParameterException {
|
||||||
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384r1");
|
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384k1");
|
||||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
|
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
|
||||||
generator.initialize(ecGenSpec, SecurityHelper.newRandom());
|
generator.initialize(ecGenSpec, SecurityHelper.newRandom());
|
||||||
KeyPair pair = generator.generateKeyPair();
|
KeyPair pair = generator.generateKeyPair();
|
||||||
|
@ -105,7 +105,7 @@ public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorC
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair generateKeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
public KeyPair generateKeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384r1");
|
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384k1");
|
||||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
|
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
|
||||||
generator.initialize(ecGenSpec, SecurityHelper.newRandom());
|
generator.initialize(ecGenSpec, SecurityHelper.newRandom());
|
||||||
return generator.generateKeyPair();
|
return generator.generateKeyPair();
|
||||||
|
|
Loading…
Reference in a new issue