Certificates.java

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
Location : issuer
Killed by : io.earcam.utilitarian.security.CertificatesTest.canSignOtherCertificates()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::issuer to ( if (x != null) null else throw new RuntimeException ) → KILLED

88

1.1
Location : subject
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::subject to ( if (x != null) null else throw new RuntimeException ) → KILLED

94

1.1
Location : serial
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::serial to ( if (x != null) null else throw new RuntimeException ) → KILLED

101

1.1
Location : serial
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::serial to ( if (x != null) null else throw new RuntimeException ) → KILLED

108

1.1
Location : canSignOtherCertificates
Killed by : io.earcam.utilitarian.security.CertificatesTest.canSignOtherCertificates()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::canSignOtherCertificates to ( if (x != null) null else throw new RuntimeException ) → KILLED

115

1.1
Location : key
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::key to ( if (x != null) null else throw new RuntimeException ) → KILLED

122

1.1
Location : signedBy
Killed by : io.earcam.utilitarian.security.CertificatesTest.signedBy()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::signedBy to ( if (x != null) null else throw new RuntimeException ) → KILLED

129

1.1
Location : validFrom
Killed by : io.earcam.utilitarian.security.CertificatesTest.validFrom()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::validFrom to ( if (x != null) null else throw new RuntimeException ) → KILLED

137

1.1
Location : validFor
Killed by : io.earcam.utilitarian.security.CertificatesTest.validFor()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::validFor to ( if (x != null) null else throw new RuntimeException ) → KILLED

150

1.1
Location : toX509
Killed by : io.earcam.utilitarian.security.CertificatesTest.validFor()
Replaced long addition with subtraction → KILLED

160

1.1
Location : toX509
Killed by : io.earcam.utilitarian.security.CertificatesTest.canSignOtherCertificates()
removed call to io/earcam/unexceptional/Exceptional::accept → KILLED

165

1.1
Location : toX509
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::toX509 to ( if (x != null) null else throw new RuntimeException ) → KILLED

171

1.1
Location : javaDate
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::javaDate to ( if (x != null) null else throw new RuntimeException ) → KILLED

177

1.1
Location : localDate
Killed by : io.earcam.utilitarian.security.CertificatesTest.validFrom()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::localDate to ( if (x != null) null else throw new RuntimeException ) → KILLED

199

1.1
Location : addCnIfMissing
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
negated conditional → KILLED

2.2
Location : addCnIfMissing
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::addCnIfMissing to ( if (x != null) null else throw new RuntimeException ) → KILLED

207

1.1
Location : sign
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::sign to ( if (x != null) null else throw new RuntimeException ) → KILLED

214

1.1
Location : toPem
Killed by : io.earcam.utilitarian.security.CertificatesTest.pemHasHeaderAndFooter()
removed call to io/earcam/utilitarian/security/Certificates$CertificateBuilder::toPem → KILLED

215

1.1
Location : toPem
Killed by : io.earcam.utilitarian.security.CertificatesTest.pemHasHeaderAndFooter()
mutated return of Object value for io/earcam/utilitarian/security/Certificates$CertificateBuilder::toPem to ( if (x != null) null else throw new RuntimeException ) → KILLED

221

1.1
Location : toPem
Killed by : io.earcam.utilitarian.security.CertificatesTest.pemHasHeaderAndFooter()
removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED

233

1.1
Location : certificate
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED

240

1.1
Location : certificate
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED

246

1.1
Location : certificate
Killed by : io.earcam.utilitarian.security.CertificatesTest.serial()
mutated return of Object value for io/earcam/utilitarian/security/Certificates::certificate to ( if (x != null) null else throw new RuntimeException ) → KILLED

252

1.1
Location : localhostCertificate
Killed by : io.earcam.utilitarian.security.CertificatesTest.localhost()
mutated return of Object value for io/earcam/utilitarian/security/Certificates::localhostCertificate to ( if (x != null) null else throw new RuntimeException ) → KILLED

269

1.1
Location : hostCertificate
Killed by : io.earcam.utilitarian.security.CertificatesTest.localhost()
mutated return of Object value for io/earcam/utilitarian/security/Certificates::hostCertificate to ( if (x != null) null else throw new RuntimeException ) → KILLED

Active mutators

Tests examined


Report generated by PIT 1.4.3