JAL-3718 Added digestonly key to getdown.txt to copy file to alt or release when...
[jalview.git] / getdown / src / getdown / core / src / main / java / com / threerings / getdown / tools / Digester.java
1 //
2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
5
6 package com.threerings.getdown.tools;
7
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.FileOutputStream;
11 import java.io.IOException;
12
13 import java.security.GeneralSecurityException;
14 import java.security.KeyStore;
15 import java.security.PrivateKey;
16 import java.security.Signature;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import com.threerings.getdown.data.Application;
22 import com.threerings.getdown.data.Digest;
23 import com.threerings.getdown.data.EnvConfig;
24 import com.threerings.getdown.data.Resource;
25 import com.threerings.getdown.util.Base64;
26
27 import static java.nio.charset.StandardCharsets.UTF_8;
28
29 /**
30  * Handles the generation of the digest.txt file.
31  */
32 public class Digester
33 {
34     /**
35      * A command line entry point for the digester.
36      */
37     public static void main (String[] args)
38         throws IOException, GeneralSecurityException
39     {
40         switch (args.length) {
41         case 1:
42             createDigests(new File(args[0]), null, null, null);
43             break;
44         case 4:
45             createDigests(new File(args[0]), new File(args[1]), args[2], args[3]);
46             break;
47         default:
48             System.err.println("Usage: Digester app_dir [keystore_path password alias]");
49             System.exit(255);
50         }
51     }
52
53     /**
54      * Creates digest file(s) and optionally signs them if {@code keystore} is not null.
55      */
56     public static void createDigests (File appdir, File keystore, String password, String alias)
57         throws IOException, GeneralSecurityException
58     {
59         for (int version = 1; version <= Digest.VERSION; version++) {
60             createDigest(version, appdir);
61             if (keystore != null) {
62                 signDigest(version, appdir, keystore, password, alias);
63             }
64         }
65     }
66
67     /**
68      * Creates a digest file in the specified application directory.
69      */
70     public static void createDigest (int version, File appdir)
71         throws IOException
72     {
73         File target = new File(appdir, Digest.digestFile(version));
74         System.out.println("Generating digest file '" + target + "'...");
75
76         // create our application and instruct it to parse its business
77         Application app = new Application(new EnvConfig(appdir));
78         app.init(false);
79
80         List<Resource> rsrcs = new ArrayList<>();
81         rsrcs.add(app.getConfigResource());
82         rsrcs.addAll(app.getCodeResources());
83         rsrcs.addAll(app.getResources());
84         rsrcs.addAll(app.getDigestOnly());
85         for (Application.AuxGroup ag : app.getAuxGroups()) {
86             rsrcs.addAll(ag.codes);
87             rsrcs.addAll(ag.rsrcs);
88         }
89
90         // now generate the digest file
91         Digest.createDigest(version, rsrcs, target);
92     }
93
94     /**
95      * Creates a digest file in the specified application directory.
96      */
97     public static void signDigest (int version, File appdir,
98                                    File storePath, String storePass, String storeAlias)
99         throws IOException, GeneralSecurityException
100     {
101         String filename = Digest.digestFile(version);
102         File inputFile = new File(appdir, filename);
103         File signatureFile = new File(appdir, filename + Application.SIGNATURE_SUFFIX);
104
105         try (FileInputStream storeInput = new FileInputStream(storePath);
106              FileInputStream dataInput = new FileInputStream(inputFile);
107              FileOutputStream signatureOutput = new FileOutputStream(signatureFile)) {
108
109             // initialize the keystore
110             KeyStore store = KeyStore.getInstance("JKS");
111             store.load(storeInput, storePass.toCharArray());
112             PrivateKey key = (PrivateKey)store.getKey(storeAlias, storePass.toCharArray());
113
114             // sign the digest file
115             String algo = Digest.sigAlgorithm(version);
116             Signature sig = Signature.getInstance(algo);
117             byte[] buffer = new byte[8192];
118             int length;
119
120             sig.initSign(key);
121             while ((length = dataInput.read(buffer)) != -1) {
122                 sig.update(buffer, 0, length);
123             }
124
125             // Write out the signature
126             String signed = Base64.encodeToString(sig.sign(), Base64.DEFAULT);
127             signatureOutput.write(signed.getBytes(UTF_8));
128         }
129     }
130 }