1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package io.earcam.instrumental.archive.sign;
20
21
22 import static io.earcam.instrumental.archive.Archive.archive;
23 import static io.earcam.instrumental.archive.AsJar.asJar;
24 import static io.earcam.instrumental.archive.sign.StandardDigestAlgorithms.SHA512;
25 import static io.earcam.instrumental.archive.sign.StandardSignatureAlgorithms.SHA256_WITH_RSA;
26 import static io.earcam.instrumental.archive.sign.WithDigest.withDigest;
27 import static io.earcam.instrumental.archive.sign.WithSignature.withSignature;
28 import static io.earcam.utilitarian.security.Certificates.certificate;
29 import static io.earcam.utilitarian.security.KeyStores.keyStore;
30
31 import static org.hamcrest.MatcherAssert.assertThat;
32 import static org.hamcrest.Matchers.*;
33
34 import java.net.URLClassLoader;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.security.KeyPair;
38 import java.security.KeyStore;
39 import java.security.cert.X509Certificate;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.UUID;
43
44 import org.junit.jupiter.api.BeforeAll;
45 import org.junit.jupiter.api.Test;
46
47 import io.earcam.acme.AmSigned;
48 import io.earcam.acme.in.intentionally.very.lengthy.packagename.safe72.ok.AmAlsoSigned;
49 import io.earcam.instrumental.archive.ArchiveConstruction;
50 import io.earcam.instrumental.lade.ClassLoaders;
51 import io.earcam.instrumental.lade.InMemoryClassLoader;
52 import io.earcam.utilitarian.io.IoStreams;
53 import io.earcam.utilitarian.security.Keys;
54 import io.earcam.utilitarian.security.Signatures;
55
56
57
58
59
60
61
62
63
64
65
66 public class WithSignatureTest {
67
68 private static final Path TEST_DIR = Paths.get(".", "target", "test", UUID.randomUUID().toString());
69
70
71 private static final KeyPair keys = Keys.rsa();
72
73 private static final String alias = "alias";
74 private static final char[] password = "password".toCharArray();
75 private static final String subject = "subject";
76
77 private static final X509Certificate certificate = certificate(keys, subject).toX509();
78 private static final KeyStore keyStore = keyStore(alias, password, keys, certificate);
79
80
81
82 @BeforeAll
83 public static void initialize()
84 {
85 TEST_DIR.toFile().mkdirs();
86 }
87
88
89 @Test
90 public void amSignedOnFilesystem() throws Exception
91 {
92 Path file = TEST_DIR.resolve("am-signed.jar");
93
94 createSignedJar().to(file);
95
96 try(URLClassLoader loader = ClassLoaders.selfFirstClassLoader(file)) {
97 Class<?> loaded = loader.loadClass(cn(AmAlsoSigned.class));
98
99 Object[] signers = loaded.getSigners();
100
101 assertThat(signers, is(not(nullValue())));
102 assertThat(signers, is(not(emptyArray())));
103
104 assertThat(Arrays.toString(signers), hasToString(allOf(
105 containsString("CN=acme"),
106 containsString("CN=subject"),
107 containsStringIgnoringCase("Signature Algorithm: SHA256withRSA"))));
108
109 byte[] bytes = IoStreams.readAllBytes(loaded.getResourceAsStream("/META-INF/SIG.RSA"));
110 List<X509Certificate> certificates = Signatures.certificatesFromSignature(bytes);
111
112 assertThat(certificates, contains(certificate));
113 }
114 }
115
116
117 @Test
118 public void amSignedInMemory() throws Exception
119 {
120 byte[] jarBytes = createSignedJar().toByteArray();
121
122 try(InMemoryClassLoader loader = ClassLoaders.inMemoryClassLoader().jar(jarBytes)) {
123 Class<?> loaded = loader.loadClass(cn(AmAlsoSigned.class));
124
125 Object[] signers = loaded.getSigners();
126
127 assertThat(signers, is(not(nullValue())));
128 assertThat(signers, is(not(emptyArray())));
129
130 assertThat(Arrays.toString(signers), hasToString(allOf(
131 containsString("CN=acme"),
132 containsString("CN=subject"),
133 containsStringIgnoringCase("Signature Algorithm: SHA256withRSA"))));
134
135 byte[] bytes = IoStreams.readAllBytes(loader.getResourceAsStream("META-INF/SIG.RSA"));
136 List<X509Certificate> certificates = Signatures.certificatesFromSignature(bytes);
137
138 assertThat(certificates, contains(certificate));
139 }
140 }
141
142
143
144 private ArchiveConstruction createSignedJar()
145 {
146 return archive()
147 .configured(asJar())
148 .configured(withSignature()
149 .digestedBy(SHA512)
150 .store(keyStore)
151 .alias(alias)
152 .password(password)
153 .signatureAlgorithm(SHA256_WITH_RSA)
154 .signatureFileName("SIG")
155 .createdBy("necessity"))
156 .with(AmSigned.class)
157 .with(AmAlsoSigned.class);
158 }
159
160
161
162 private static String cn(Class<?> type)
163 {
164 return type.getTypeName();
165 }
166
167
168 @Test
169 public void amNotSignedInMemory() throws Exception
170 {
171 byte[] jarBytes = createDigestedButNotSignedJar().toByteArray();
172
173 try(InMemoryClassLoader loader = ClassLoaders.inMemoryClassLoader().jar(jarBytes)) {
174 Class<?> loaded = loader.loadClass(cn(AmAlsoSigned.class));
175
176 assertThat(loaded.getSigners(), is(nullValue()));
177 }
178 }
179
180
181 private ArchiveConstruction createDigestedButNotSignedJar()
182 {
183 return archive()
184 .configured(withDigest()
185 .digestedBy(SHA512))
186 .with(AmSigned.class)
187 .with(AmAlsoSigned.class);
188 }
189
190
191 @Test
192 public void amNotSignedOnFilesystem() throws Exception
193 {
194 Path file = TEST_DIR.resolve("am-not-signed.jar");
195 createDigestedButNotSignedJar().to(file);
196 try(URLClassLoader loader = ClassLoaders.selfFirstClassLoader(file)) {
197 Class<?> loaded = loader.loadClass(cn(AmAlsoSigned.class));
198
199 assertThat(loaded.getSigners(), is(nullValue()));
200 }
201 }
202 }