1 | /* | |
2 | * $Id: TSAClientBouncyCastle.java 4065 2009-09-16 23:09:11Z psoares33 $ | |
3 | * | |
4 | * Copyright 2009 Martin Brunecky, Aiken Sam | |
5 | * | |
6 | * The contents of this file are subject to the Mozilla Public License Version 1.1 | |
7 | * (the "License"); you may not use this file except in compliance with the License. | |
8 | * You may obtain a copy of the License at http://www.mozilla.org/MPL/ | |
9 | * | |
10 | * Software distributed under the License is distributed on an "AS IS" basis, | |
11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 | * for the specific language governing rights and limitations under the License. | |
13 | * | |
14 | * The Original Code is 'iText, a free JAVA-PDF library'. | |
15 | * | |
16 | * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by | |
17 | * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie. | |
18 | * All Rights Reserved. | |
19 | * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer | |
20 | * are Copyright (C) 2009 by Martin Brunecky. All Rights Reserved. | |
21 | * | |
22 | * Contributor(s): all the names of the contributors are added in the source code | |
23 | * where applicable. | |
24 | * | |
25 | * Alternatively, the contents of this file may be used under the terms of the | |
26 | * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the | |
27 | * provisions of LGPL are applicable instead of those above. If you wish to | |
28 | * allow use of your version of this file only under the terms of the LGPL | |
29 | * License and not to allow others to use your version of this file under | |
30 | * the MPL, indicate your decision by deleting the provisions above and | |
31 | * replace them with the notice and other provisions required by the LGPL. | |
32 | * If you do not delete the provisions above, a recipient may use your version | |
33 | * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. | |
34 | * | |
35 | * This library is free software; you can redistribute it and/or modify it | |
36 | * under the terms of the MPL as stated above or under the terms of the GNU | |
37 | * Library General Public License as published by the Free Software Foundation; | |
38 | * either version 2 of the License, or any later version. | |
39 | * | |
40 | * This library is distributed in the hope that it will be useful, but WITHOUT | |
41 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
42 | * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more | |
43 | * details. | |
44 | * | |
45 | * If you didn't download this code from the following link, you should check if | |
46 | * you aren't using an obsolete version: | |
47 | * http://www.lowagie.com/iText/ | |
48 | */ | |
49 | ||
50 | package com.lowagie.text.pdf; | |
51 | ||
52 | import java.io.ByteArrayOutputStream; | |
53 | import java.io.InputStream; | |
54 | import java.io.OutputStream; | |
55 | import java.math.BigInteger; | |
56 | import java.net.URL; | |
57 | import java.net.URLConnection; | |
58 | import java.util.Base64; | |
59 | ||
60 | import org.bouncycastle.asn1.cmp.PKIFailureInfo; | |
61 | import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; | |
62 | import org.bouncycastle.tsp.TimeStampRequest; | |
63 | import org.bouncycastle.tsp.TimeStampRequestGenerator; | |
64 | import org.bouncycastle.tsp.TimeStampResponse; | |
65 | import org.bouncycastle.tsp.TimeStampToken; | |
66 | import org.bouncycastle.tsp.TimeStampTokenInfo; | |
67 | ||
68 | import com.lowagie.text.error_messages.MessageLocalization; | |
69 | ||
70 | /** | |
71 | * Time Stamp Authority Client interface implementation using Bouncy Castle | |
72 | * org.bouncycastle.tsp package. | |
73 | * <p> | |
74 | * Created by Aiken Sam, 2006-11-15, refactored by Martin Brunecky, 07/15/2007 | |
75 | * for ease of subclassing. | |
76 | * </p> | |
77 | * | |
78 | * @since 2.1.6 | |
79 | */ | |
80 | public class TSAClientBouncyCastle implements TSAClient { | |
81 | /** URL of the Time Stamp Authority */ | |
82 | protected String tsaURL; | |
83 | /** TSA Username */ | |
84 | protected String tsaUsername; | |
85 | /** TSA password */ | |
86 | protected String tsaPassword; | |
87 | /** Estimate of the received time stamp token */ | |
88 | protected int tokSzEstimate; | |
89 | ||
90 | /** | |
91 | * Creates an instance of a TSAClient that will use BouncyCastle. | |
92 | * | |
93 | * @param url | |
94 | * String - Time Stamp Authority URL (i.e. | |
95 | * "http://tsatest1.digistamp.com/TSA") | |
96 | */ | |
97 | public TSAClientBouncyCastle(String url) { | |
98 | this(url, null, null, 4096); | |
99 | } | |
100 | ||
101 | /** | |
102 | * Creates an instance of a TSAClient that will use BouncyCastle. | |
103 | * | |
104 | * @param url | |
105 | * String - Time Stamp Authority URL (i.e. | |
106 | * "http://tsatest1.digistamp.com/TSA") | |
107 | * @param username | |
108 | * String - user(account) name | |
109 | * @param password | |
110 | * String - password | |
111 | */ | |
112 | public TSAClientBouncyCastle(String url, String username, String password) { | |
113 | this(url, username, password, 4096); | |
114 | } | |
115 | ||
116 | /** | |
117 | * Constructor. Note the token size estimate is updated by each call, as the | |
118 | * token size is not likely to change (as long as we call the same TSA using | |
119 | * the same imprint length). | |
120 | * | |
121 | * @param url | |
122 | * String - Time Stamp Authority URL (i.e. | |
123 | * "http://tsatest1.digistamp.com/TSA") | |
124 | * @param username | |
125 | * String - user(account) name | |
126 | * @param password | |
127 | * String - password | |
128 | * @param tokSzEstimate | |
129 | * int - estimated size of received time stamp token (DER encoded) | |
130 | */ | |
131 | public TSAClientBouncyCastle(String url, String username, String password, | |
132 | int tokSzEstimate) { | |
133 | this.tsaURL = url; | |
134 | this.tsaUsername = username; | |
135 | this.tsaPassword = password; | |
136 | this.tokSzEstimate = tokSzEstimate; | |
137 | } | |
138 | ||
139 | /** | |
140 | * Get the token size estimate. Returned value reflects the result of the last | |
141 | * succesfull call, padded | |
142 | * | |
143 | * @return an estimate of the token size | |
144 | */ | |
145 | @Override | |
146 | public int getTokenSizeEstimate() { | |
147 | return tokSzEstimate; | |
148 | } | |
149 | ||
150 | /** | |
151 | * Get RFC 3161 timeStampToken. Method may return null indicating that | |
152 | * timestamp should be skipped. | |
153 | * | |
154 | * @param caller | |
155 | * PdfPKCS7 - calling PdfPKCS7 instance (in case caller needs it) | |
156 | * @param imprint | |
157 | * byte[] - data imprint to be time-stamped | |
158 | * @return byte[] - encoded, TSA signed data of the timeStampToken | |
159 | * @throws Exception | |
160 | * - TSA request failed | |
161 | * @see com.lowagie.text.pdf.TSAClient#getTimeStampToken(com.lowagie.text.pdf.PdfPKCS7, | |
162 | * byte[]) | |
163 | */ | |
164 | @Override | |
165 | public byte[] getTimeStampToken(PdfPKCS7 caller, byte[] imprint) | |
166 | throws Exception { | |
167 | return getTimeStampToken(imprint); | |
168 | } | |
169 | ||
170 | /** | |
171 | * Get timestamp token - Bouncy Castle request encoding / decoding layer | |
172 | */ | |
173 | protected byte[] getTimeStampToken(byte[] imprint) throws Exception { | |
174 | byte[] respBytes = null; | |
175 | try { | |
176 | // Setup the time stamp request | |
177 | TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator(); | |
178 | tsqGenerator.setCertReq(true); | |
179 | // tsqGenerator.setReqPolicy("1.3.6.1.4.1.601.10.3.1"); | |
180 | BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis()); | |
181 | TimeStampRequest request = tsqGenerator.generate( | |
182 | X509ObjectIdentifiers.id_SHA1.getId(), imprint, nonce); | |
183 | byte[] requestBytes = request.getEncoded(); | |
184 | ||
185 | // Call the communications layer | |
186 | respBytes = getTSAResponse(requestBytes); | |
187 | ||
188 | // Handle the TSA response | |
189 | TimeStampResponse response = new TimeStampResponse(respBytes); | |
190 | ||
191 | // validate communication level attributes (RFC 3161 PKIStatus) | |
192 | response.validate(request); | |
193 | PKIFailureInfo failure = response.getFailInfo(); | |
194 | int value = (failure == null) ? 0 : failure.intValue(); | |
195 |
1
1. getTimeStampToken : negated conditional → NO_COVERAGE |
if (value != 0) { |
196 | // @todo: Translate value of 15 error codes defined by | |
197 | // PKIFailureInfo to string | |
198 | throw new Exception(MessageLocalization.getComposedMessage( | |
199 | "invalid.tsa.1.response.code.2", tsaURL, String.valueOf(value))); | |
200 | } | |
201 | // @todo: validate the time stap certificate chain (if we want | |
202 | // assure we do not sign using an invalid timestamp). | |
203 | ||
204 | // extract just the time stamp token (removes communication status | |
205 | // info) | |
206 | TimeStampToken tsToken = response.getTimeStampToken(); | |
207 |
1
1. getTimeStampToken : negated conditional → NO_COVERAGE |
if (tsToken == null) { |
208 | throw new Exception(MessageLocalization.getComposedMessage( | |
209 | "tsa.1.failed.to.return.time.stamp.token.2", tsaURL, | |
210 | response.getStatusString())); | |
211 | } | |
212 | TimeStampTokenInfo info = tsToken.getTimeStampInfo(); // to view | |
213 | // details | |
214 | byte[] encoded = tsToken.getEncoded(); | |
215 | long stop = System.currentTimeMillis(); | |
216 | ||
217 | // Update our token size estimate for the next call (padded to be | |
218 | // safe) | |
219 |
1
1. getTimeStampToken : Replaced integer addition with subtraction → NO_COVERAGE |
this.tokSzEstimate = encoded.length + 32; |
220 |
1
1. getTimeStampToken : mutated return of Object value for com/lowagie/text/pdf/TSAClientBouncyCastle::getTimeStampToken to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return encoded; |
221 | } catch (Exception e) { | |
222 | throw e; | |
223 | } catch (Throwable t) { | |
224 | throw new Exception(MessageLocalization.getComposedMessage( | |
225 | "failed.to.get.tsa.response.from.1", tsaURL), t); | |
226 | } | |
227 | } | |
228 | ||
229 | /** | |
230 | * Get timestamp token - communications layer | |
231 | * | |
232 | * @return - byte[] - TSA response, raw bytes (RFC 3161 encoded) | |
233 | */ | |
234 | protected byte[] getTSAResponse(byte[] requestBytes) throws Exception { | |
235 | // Setup the TSA connection | |
236 | URL url = new URL(tsaURL); | |
237 | URLConnection tsaConnection; | |
238 | tsaConnection = url.openConnection(); | |
239 | ||
240 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setDoInput → NO_COVERAGE |
tsaConnection.setDoInput(true); |
241 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setDoOutput → NO_COVERAGE |
tsaConnection.setDoOutput(true); |
242 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setUseCaches → NO_COVERAGE |
tsaConnection.setUseCaches(false); |
243 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setRequestProperty → NO_COVERAGE |
tsaConnection.setRequestProperty("Content-Type", |
244 | "application/timestamp-query"); | |
245 | // tsaConnection.setRequestProperty("Content-Transfer-Encoding", | |
246 | // "base64"); | |
247 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setRequestProperty → NO_COVERAGE |
tsaConnection.setRequestProperty("Content-Transfer-Encoding", "binary"); |
248 | ||
249 |
2
1. getTSAResponse : negated conditional → NO_COVERAGE 2. getTSAResponse : negated conditional → NO_COVERAGE |
if ((tsaUsername != null) && !tsaUsername.equals("")) { |
250 | String userPassword = tsaUsername + ":" + tsaPassword; | |
251 |
1
1. getTSAResponse : removed call to java/net/URLConnection::setRequestProperty → NO_COVERAGE |
tsaConnection.setRequestProperty("Authorization", "Basic " |
252 | + new String(Base64.getEncoder().encode(userPassword.getBytes()))); | |
253 | } | |
254 | OutputStream out = tsaConnection.getOutputStream(); | |
255 |
1
1. getTSAResponse : removed call to java/io/OutputStream::write → NO_COVERAGE |
out.write(requestBytes); |
256 |
1
1. getTSAResponse : removed call to java/io/OutputStream::close → NO_COVERAGE |
out.close(); |
257 | ||
258 | // Get TSA response as a byte array | |
259 | InputStream inp = tsaConnection.getInputStream(); | |
260 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
261 | byte[] buffer = new byte[1024]; | |
262 | int bytesRead = 0; | |
263 |
2
1. getTSAResponse : changed conditional boundary → NO_COVERAGE 2. getTSAResponse : negated conditional → NO_COVERAGE |
while ((bytesRead = inp.read(buffer, 0, buffer.length)) >= 0) { |
264 |
1
1. getTSAResponse : removed call to java/io/ByteArrayOutputStream::write → NO_COVERAGE |
baos.write(buffer, 0, bytesRead); |
265 | } | |
266 | byte[] respBytes = baos.toByteArray(); | |
267 | ||
268 | String encoding = tsaConnection.getContentEncoding(); | |
269 |
2
1. getTSAResponse : negated conditional → NO_COVERAGE 2. getTSAResponse : negated conditional → NO_COVERAGE |
if (encoding != null && encoding.equalsIgnoreCase("base64")) { |
270 | respBytes = Base64.getDecoder().decode(respBytes); | |
271 | } | |
272 |
1
1. getTSAResponse : mutated return of Object value for com/lowagie/text/pdf/TSAClientBouncyCastle::getTSAResponse to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return respBytes; |
273 | } | |
274 | } | |
Mutations | ||
195 |
1.1 |
|
207 |
1.1 |
|
219 |
1.1 |
|
220 |
1.1 |
|
240 |
1.1 |
|
241 |
1.1 |
|
242 |
1.1 |
|
243 |
1.1 |
|
247 |
1.1 |
|
249 |
1.1 2.2 |
|
251 |
1.1 |
|
255 |
1.1 |
|
256 |
1.1 |
|
263 |
1.1 2.2 |
|
264 |
1.1 |
|
269 |
1.1 2.2 |
|
272 |
1.1 |