1 | /*- | |
2 | * #%L | |
3 | * io.earcam.utilitarian.security | |
4 | * %% | |
5 | * Copyright (C) 2017 - 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.utilitarian.security; | |
20 | ||
21 | import java.io.StringWriter; | |
22 | import java.io.Writer; | |
23 | import java.math.BigInteger; | |
24 | import java.security.KeyPair; | |
25 | import java.security.cert.X509Certificate; | |
26 | import java.time.LocalDate; | |
27 | import java.time.ZoneId; | |
28 | import java.util.Date; | |
29 | import java.util.Objects; | |
30 | import java.util.concurrent.TimeUnit; | |
31 | ||
32 | import javax.annotation.ParametersAreNonnullByDefault; | |
33 | ||
34 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; | |
35 | import org.bouncycastle.asn1.x500.X500Name; | |
36 | import org.bouncycastle.asn1.x509.BasicConstraints; | |
37 | import org.bouncycastle.cert.CertIOException; | |
38 | import org.bouncycastle.cert.X509CertificateHolder; | |
39 | import org.bouncycastle.cert.X509v3CertificateBuilder; | |
40 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | |
41 | import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; | |
42 | import org.bouncycastle.jce.X509KeyUsage; | |
43 | import org.bouncycastle.jce.provider.BouncyCastleProvider; | |
44 | import org.bouncycastle.openssl.jcajce.JcaPEMWriter; | |
45 | import org.bouncycastle.operator.ContentSigner; | |
46 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; | |
47 | ||
48 | import io.earcam.unexceptional.Closing; | |
49 | import io.earcam.unexceptional.Exceptional; | |
50 | ||
51 | @ParametersAreNonnullByDefault | |
52 | public class Certificates { | |
53 | ||
54 | private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider(); | |
55 | static final String DN_LOCALHOST = "DN=localhost, L=London, C=GB"; | |
56 | ||
57 | public static class CertificateBuilder { | |
58 | ||
59 | @SuppressWarnings("squid:S1313") // SonarQube false-positive; not an IP address | |
60 | private static final String EXTENSION_KEY_USAGE = "2.5.29.15"; | |
61 | @SuppressWarnings("squid:S1313") // SonarQube false-positive; not an IP address | |
62 | static final String EXTENSION_MAY_ACT_AS_CA = "2.5.29.19"; | |
63 | private String issuerName = "acme"; | |
64 | private String subjectName; | |
65 | private BigInteger serial = BigInteger.ONE; | |
66 | private boolean canSignOtherCertificates = false; | |
67 | private LocalDate validFrom = LocalDate.now(ZoneId.systemDefault()); | |
68 | private long duration = 365; | |
69 | private TimeUnit unit = TimeUnit.DAYS; | |
70 | private String signatureAlgorithm = "SHA256withRSA"; | |
71 | private KeyPair keyPair; | |
72 | ||
73 | ||
74 | CertificateBuilder() | |
75 | {} | |
76 | ||
77 | ||
78 | public CertificateBuilder issuer(String name) | |
79 | { | |
80 | issuerName = name; | |
81 |
1
1. issuer : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::issuer to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
82 | } | |
83 | ||
84 | ||
85 | public CertificateBuilder subject(String name) | |
86 | { | |
87 | subjectName = name; | |
88 |
1
1. subject : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::subject to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
89 | } | |
90 | ||
91 | ||
92 | public CertificateBuilder serial(int number) | |
93 | { | |
94 |
1
1. serial : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::serial to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return serial(BigInteger.valueOf(number)); |
95 | } | |
96 | ||
97 | ||
98 | public CertificateBuilder serial(BigInteger number) | |
99 | { | |
100 | this.serial = number; | |
101 |
1
1. serial : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::serial to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
102 | } | |
103 | ||
104 | ||
105 | public CertificateBuilder canSignOtherCertificates() | |
106 | { | |
107 | canSignOtherCertificates = true; | |
108 |
1
1. canSignOtherCertificates : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::canSignOtherCertificates to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
109 | } | |
110 | ||
111 | ||
112 | public CertificateBuilder key(KeyPair pair) | |
113 | { | |
114 | this.keyPair = pair; | |
115 |
1
1. key : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::key to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
116 | } | |
117 | ||
118 | ||
119 | public CertificateBuilder signedBy(String signatureAlgorithm) | |
120 | { | |
121 | this.signatureAlgorithm = signatureAlgorithm; | |
122 |
1
1. signedBy : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::signedBy to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
123 | } | |
124 | ||
125 | ||
126 | public CertificateBuilder validFrom(LocalDate from) | |
127 | { | |
128 | validFrom = from; | |
129 |
1
1. validFrom : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::validFrom to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
130 | } | |
131 | ||
132 | ||
133 | public CertificateBuilder validFor(long duration, TimeUnit unit) | |
134 | { | |
135 | this.duration = duration; | |
136 | this.unit = unit; | |
137 |
1
1. validFor : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::validFor to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return this; |
138 | } | |
139 | ||
140 | ||
141 | public X509Certificate toX509() | |
142 | { | |
143 | Objects.requireNonNull(keyPair, "keyPair"); | |
144 | Objects.requireNonNull(issuerName, "issuerName"); | |
145 | Objects.requireNonNull(subjectName, "subjectName"); | |
146 | X500Name issuer = new X500Name(addCnIfMissing(issuerName)); | |
147 | X500Name subject = new X500Name(addCnIfMissing(subjectName)); | |
148 | ||
149 | Date from = javaDate(validFrom); | |
150 |
1
1. toX509 : Replaced long addition with subtraction → KILLED |
Date to = new Date(from.getTime() + unit.toMillis(duration)); |
151 | ||
152 | X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( | |
153 | issuer, | |
154 | serial, | |
155 | from, | |
156 | to, | |
157 | subject, | |
158 | keyPair.getPublic()); | |
159 | ||
160 |
1
1. toX509 : removed call to io/earcam/unexceptional/Exceptional::accept → KILLED |
Exceptional.accept(this::addExtensions, certificateBuilder); |
161 | ||
162 | X509CertificateHolder signed = sign(keyPair, signatureAlgorithm, certificateBuilder); | |
163 | ||
164 | JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(PROVIDER); | |
165 |
1
1. toX509 : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::toX509 to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return Exceptional.apply(converter::getCertificate, signed); |
166 | } | |
167 | ||
168 | ||
169 | static Date javaDate(LocalDate date) | |
170 | { | |
171 |
1
1. javaDate : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::javaDate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return java.sql.Date.valueOf(date); |
172 | } | |
173 | ||
174 | ||
175 | static LocalDate localDate(Date date) | |
176 | { | |
177 |
1
1. localDate : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::localDate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new java.sql.Date(date.getTime()).toLocalDate(); |
178 | } | |
179 | ||
180 | ||
181 | private void addExtensions(X509v3CertificateBuilder certificateBuilder) throws CertIOException | |
182 | { | |
183 | certificateBuilder.addExtension( | |
184 | new ASN1ObjectIdentifier(EXTENSION_MAY_ACT_AS_CA), | |
185 | false, | |
186 | new BasicConstraints(canSignOtherCertificates)).addExtension( | |
187 | new ASN1ObjectIdentifier(EXTENSION_KEY_USAGE), | |
188 | true, | |
189 | new X509KeyUsage( | |
190 | X509KeyUsage.digitalSignature | | |
191 | X509KeyUsage.nonRepudiation | | |
192 | X509KeyUsage.keyEncipherment | | |
193 | X509KeyUsage.dataEncipherment)); | |
194 | } | |
195 | ||
196 | ||
197 | private String addCnIfMissing(String name) | |
198 | { | |
199 |
2
1. addCnIfMissing : negated conditional → KILLED 2. addCnIfMissing : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::addCnIfMissing to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return (name.indexOf('=') == -1) ? "CN=" + name : name; |
200 | } | |
201 | ||
202 | ||
203 | private static X509CertificateHolder sign(KeyPair keyPair, String signatureAlgorithm, X509v3CertificateBuilder certificateBuilder) | |
204 | { | |
205 | JcaContentSignerBuilder jcaContentSignerBuilder = new JcaContentSignerBuilder(signatureAlgorithm); | |
206 | ContentSigner sigGen = Exceptional.apply(jcaContentSignerBuilder::build, keyPair.getPrivate()); | |
207 |
1
1. sign : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::sign to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return certificateBuilder.build(sigGen); |
208 | } | |
209 | ||
210 | ||
211 | public String toPem() | |
212 | { | |
213 | StringWriter writer = new StringWriter(); | |
214 |
1
1. toPem : removed call to io/earcam/utilitarian/security/Certificates$CertificateBuilder::toPem → KILLED |
toPem(writer); |
215 |
1
1. toPem : mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::toPem to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return writer.toString(); |
216 | } | |
217 | ||
218 | ||
219 | public void toPem(Writer writer) | |
220 | { | |
221 |
1
1. toPem : removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED |
Closing.closeAfterAccepting(JcaPEMWriter::new, writer, toX509(), JcaPEMWriter::writeObject); |
222 | } | |
223 | ||
224 | } | |
225 | ||
226 | ||
227 | private Certificates() | |
228 | {} | |
229 | ||
230 | ||
231 | public static CertificateBuilder certificate(KeyPair pair, String subjectName) | |
232 | { | |
233 |
1
1. certificate : mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return certificate(pair) |
234 | .subject(subjectName); | |
235 | } | |
236 | ||
237 | ||
238 | public static CertificateBuilder certificate(KeyPair pair) | |
239 | { | |
240 |
1
1. certificate : mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return certificate().key(pair); |
241 | } | |
242 | ||
243 | ||
244 | public static CertificateBuilder certificate() | |
245 | { | |
246 |
1
1. certificate : mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new CertificateBuilder(); |
247 | } | |
248 | ||
249 | ||
250 | public static X509Certificate localhostCertificate(KeyPair keys) | |
251 | { | |
252 |
1
1. localhostCertificate : mutated return of Object value for io/earcam/utilitarian/security/Certificates::localhostCertificate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return hostCertificate(keys, DN_LOCALHOST); |
253 | } | |
254 | ||
255 | ||
256 | /** | |
257 | * @deprecated | |
258 | * @see #localhostCertificate(KeyPair) | |
259 | */ | |
260 | @Deprecated | |
261 | public static X509Certificate hostCertificate(KeyPair keys) | |
262 | { | |
263 | throw new UnsupportedOperationException(); | |
264 | } | |
265 | ||
266 | ||
267 | public static X509Certificate hostCertificate(KeyPair keys, String hostname) | |
268 | { | |
269 |
1
1. hostCertificate : mutated return of Object value for io/earcam/utilitarian/security/Certificates::hostCertificate to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return certificate(keys) |
270 | .subject(hostname) | |
271 | .toX509(); | |
272 | } | |
273 | } | |
Mutations | ||
81 |
1.1 |
|
88 |
1.1 |
|
94 |
1.1 |
|
101 |
1.1 |
|
108 |
1.1 |
|
115 |
1.1 |
|
122 |
1.1 |
|
129 |
1.1 |
|
137 |
1.1 |
|
150 |
1.1 |
|
160 |
1.1 |
|
165 |
1.1 |
|
171 |
1.1 |
|
177 |
1.1 |
|
199 |
1.1 2.2 |
|
207 |
1.1 |
|
214 |
1.1 |
|
215 |
1.1 |
|
221 |
1.1 |
|
233 |
1.1 |
|
240 |
1.1 |
|
246 |
1.1 |
|
252 |
1.1 |
|
269 |
1.1 |