View Javadoc
1   /*-
2    * #%L
3    * io.earcam.instrumental.archive.sign
4    * %%
5    * Copyright (C) 2018 earcam
6    * %%
7    * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
8    * 
9    * You <b>must</b> choose to accept, in full - any individual or combination of 
10   * the following licenses:
11   * <ul>
12   * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
13   * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
14   * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
15   * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
16   * </ul>
17   * #L%
18   */
19  package io.earcam.instrumental.archive.sign;
20  
21  import java.security.MessageDigest;
22  import java.util.Base64;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Map.Entry;
26  import java.util.jar.Attributes;
27  import java.util.jar.Manifest;
28  
29  import io.earcam.instrumental.archive.ArchiveConfigurationPlugin;
30  import io.earcam.instrumental.archive.ArchiveRegistrar;
31  import io.earcam.instrumental.archive.ArchiveResource;
32  import io.earcam.instrumental.archive.ArchiveResourceListener;
33  import io.earcam.instrumental.archive.ManifestProcessor;
34  
35  /**
36   * <p>
37   * Adds the archive's contents as digests in the manifest.
38   * </p>
39   *
40   */
41  public class WithDigest implements ArchiveConfigurationPlugin, ManifestProcessor, ArchiveResourceListener {
42  
43  	protected MessageDigest digester;
44  	private final Map<String, String> digests = new HashMap<>();
45  	protected Manifest manifest;
46  
47  
48  	WithDigest()
49  	{}
50  
51  
52  	/**
53  	 * <p>
54  	 * withDigest.
55  	 * </p>
56  	 *
57  	 * @return a {@link io.earcam.instrumental.archive.sign.WithDigest} object.
58  	 */
59  	public static WithDigest withDigest()
60  	{
61  		return new WithDigest();
62  	}
63  
64  
65  	@Override
66  	public void attach(ArchiveRegistrar core)
67  	{
68  		core.registerManifestProcessor(this);
69  		core.registerResourceListener(this);
70  	}
71  
72  
73  	@Override
74  	public void added(ArchiveResource resource)
75  	{
76  		digests.put(resource.name(), base64Digest(resource.bytes()));
77  	}
78  
79  
80  	/**
81  	 * <p>
82  	 * base64Digest.
83  	 * </p>
84  	 *
85  	 * @param data an array of {@link byte} objects.
86  	 * @return a {@link java.lang.String} object.
87  	 */
88  	protected String base64Digest(byte[] data)
89  	{
90  		return base64Digest(digester, data);
91  	}
92  
93  
94  	static String base64Digest(MessageDigest digester, byte[] data)
95  	{
96  		digester.reset();
97  		return encodeBase64(digester.digest(data));
98  	}
99  
100 
101 	static String encodeBase64(byte[] data)
102 	{
103 		return Base64.getEncoder().encodeToString(data);
104 	}
105 
106 
107 	@Override
108 	public void process(Manifest manifest)
109 	{
110 		this.manifest = manifest;
111 
112 		Map<String, Attributes> entries = manifest.getEntries();
113 
114 		for(Entry<String, String> e : digests.entrySet()) {
115 			Attributes attributes = entries.computeIfAbsent(e.getKey(), k -> new Attributes());
116 			attributes.putValue(digester.getAlgorithm() + "-Digest", e.getValue());
117 		}
118 	}
119 
120 
121 	/* WithDigest API */
122 
123 	/**
124 	 * Adds digest entries to the manifest. Required for signed JARs.
125 	 *
126 	 * @param hash the algorithm name
127 	 * @return this archive builder
128 	 */
129 	public WithDigest digestedBy(StandardDigestAlgorithms hash)
130 	{
131 		return digestedBy(hash.create());
132 	}
133 
134 
135 	/**
136 	 * Adds digest entries to the manifest. Required for signed JARs.
137 	 *
138 	 * @param digest the hash algorithm
139 	 * @return this archive builder
140 	 */
141 	public WithDigest digestedBy(MessageDigest digest)
142 	{
143 		this.digester = digest;
144 		return this;
145 	}
146 
147 }