001// Copyright (C) 2002 IAIK 002// https://sic.tech/ 003// 004// Copyright (C) 2003 - 2025 Stiftung Secure Information and 005// Communication Technologies SIC 006// https://sic.tech/ 007// 008// All rights reserved. 009// 010// This source is provided for inspection purposes and recompilation only, 011// unless specified differently in a contract with IAIK. This source has to 012// be kept in strict confidence and must not be disclosed to any third party 013// under any circumstances. Redistribution in source and binary forms, with 014// or without modification, are <not> permitted in any case! 015// 016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 026// SUCH DAMAGE. 027// 028// $Header: /IAIK-CMS/current/src/demo/cms/tsp/TSPDemoUtils.java 9 12.02.25 17:58 Dbratko $ 029// $Revision: 9 $ 030// 031 032package demo.cms.tsp; 033 034import iaik.asn1.CodingException; 035import iaik.asn1.ObjectID; 036import iaik.asn1.structures.AlgorithmID; 037import iaik.asn1.structures.Attribute; 038import iaik.cms.CMSException; 039import iaik.cms.SignerInfo; 040import iaik.smime.attributes.SignatureTimeStampToken; 041import iaik.tsp.MessageImprint; 042import iaik.tsp.PKIFailureInfo; 043import iaik.tsp.PKIStatus; 044import iaik.tsp.PKIStatusInfo; 045import iaik.tsp.TSTInfo; 046import iaik.tsp.TimeStampReq; 047import iaik.tsp.TimeStampResp; 048import iaik.tsp.TimeStampToken; 049import iaik.tsp.TspException; 050import iaik.tsp.transport.http.TspHttpClient; 051import iaik.tsp.transport.http.TspHttpResponse; 052import iaik.utils.CryptoUtils; 053import iaik.x509.X509Certificate; 054import iaik.x509.X509ExtensionInitException; 055import iaik.x509.extensions.ExtendedKeyUsage; 056 057import java.io.IOException; 058import java.math.BigInteger; 059import java.net.URL; 060import java.security.MessageDigest; 061import java.security.NoSuchAlgorithmException; 062import java.security.cert.CertificateException; 063import java.util.Random; 064 065/** 066 * Some utils for creating and sending time stamp requests, 067 * validating responses and adding time stamp token attributes. 068 * Used by the TSP demo. 069 * 070 * 071 * @see TimeStampDemo 072 * @see TimeStampListener 073 */ 074public class TSPDemoUtils { 075 076 /** 077 * Creates a TimeStampRequest for the given SignerInfo. 078 * 079 * @param signerInfo the SignerInfo to be time stamped 080 * @param reqPolicy the policy of the TSA from which to get a response, 081 * maybe <code>null</code> if we accept any TSA 082 * 083 * @return the time stamp request just created 084 * 085 * @throws TspException if some error occurs during time stamp creation 086 */ 087 public static TimeStampReq createRequest(SignerInfo signerInfo, 088 ObjectID reqPolicy) 089 090 throws TspException { 091 092 // we have to time stamp the signature value 093 byte[] signatureValue = signerInfo.getSignatureValue(); 094 // calculate the MessageImprint 095 AlgorithmID hashAlg = (AlgorithmID)AlgorithmID.sha256.clone(); 096 MessageDigest md = null; 097 try { 098 md = hashAlg.getMessageDigestInstance(); 099 } catch (NoSuchAlgorithmException ex) { 100 throw new TspException("Cannot calculate MessageImprint! Algorithm SHA-256 not supported!"); 101 } 102 byte[] toBeTimeStamped = md.digest(signatureValue); 103 MessageImprint imprint = new MessageImprint(hashAlg, toBeTimeStamped); 104 //Create a new TimeStampReq 105 TimeStampReq request = new TimeStampReq(); 106 //set the imprint 107 request.setMessageImprint(imprint); 108 //request the TSA to include its certificate chain into the response 109 request.setCertReq(true); 110 if (reqPolicy != null) { 111 // request some particular TSA policy? 112 request.setTSAPolicyID(reqPolicy); 113 } 114 // set a Nonce 115 BigInteger nonce = new BigInteger(64, new Random()); 116 request.setNonce(nonce); 117 return request; 118 } 119 120 /** 121 * Sends the given time stamp request to the given TSA. 122 * 123 * @param request the time stamp request to be sent to the TSA 124 * @param tsaUrl the URL of the time stamp authority from which to 125 * get the time stamp 126 * 127 * @throws TspException if an error occurs during sending the request to the TSA (e.g. 128 * connecting to the TSA fails, ...) 129 * @throws NullPointerException if <code>request</code> or <code>tsaUrl</code> 130 * are <code>null</code> 131 */ 132 public static TimeStampResp sendRequest(TimeStampReq request, 133 String tsaUrl) 134 135 throws TspException { 136 137 if (request == null) { 138 throw new NullPointerException("Time stamp request must not be null!"); 139 } 140 if (tsaUrl == null) { 141 throw new NullPointerException("TSA url must not be null!"); 142 } 143 144 try { 145 // send the request to the TSA 146 TspHttpClient tspHttpClient = new TspHttpClient(new URL(tsaUrl)); 147 TspHttpResponse tspHttpResponse = tspHttpClient.sendRequest(request); 148 if (tspHttpResponse.isErrorResponse()) { 149 throw new TspException("Error connecting to TSA: " + tspHttpResponse.getErrorMsg()); 150 } 151 TimeStampResp response = tspHttpResponse.getTimeStampResp(); 152 return response; 153 } catch (IOException ex) { 154 throw new TspException("Error connecting to TSA: " + ex.toString()); 155 } catch (CodingException ex) { 156 throw new TspException("Error encoding tsp request: " + ex.getMessage()); 157 } 158 } 159 160 /** 161 * Adds a SignatureTimeStampToken attribute to the given SignerInfo. 162 * 163 * @param tspToken the time stamp token to be added as attribute 164 * @param signerInfo the SignerInfo to be time stamped 165 * 166 * @throws TspException if some error occurs when adding the attribute 167 */ 168 public static void timeStamp(TimeStampToken tspToken, 169 SignerInfo signerInfo) 170 171 throws TspException { 172 173 if (tspToken == null) { 174 throw new NullPointerException("tspToken must not be null!"); 175 } 176 if (signerInfo == null) { 177 throw new NullPointerException("signerInfo must not be null!"); 178 } 179 180 try { 181 // include TimeStampToken as unsigned attribute 182 SignatureTimeStampToken stst = new SignatureTimeStampToken(tspToken.toASN1Object()); 183 signerInfo.addUnSignedAttribute(new Attribute(stst)); 184 } catch (CodingException ex) { 185 throw new TspException("Error encoding TimeStampToken attribute: " + ex.getMessage()); 186 } catch (CMSException ex) { 187 throw new TspException("Error adding SignatureTimeStampToken attribute: " + ex.getMessage()); 188 } 189 190 } 191 192 /** 193 * Validates the response received from the TSA. 194 * 195 * @param response the time stamp response to be validated 196 * @param request the time stamp request that has been sent 197 * 198 * @throws TspException if the response is invalid (wrong MessageImprint, missing certificate,...) 199 */ 200 public static void validateResponse(TimeStampResp response, 201 TimeStampReq request) 202 203 throws TspException { 204 205 // get the status info 206 PKIStatusInfo statusInfo = response.getPKIStatusInfo(); 207 // status? 208 PKIStatus status = statusInfo.getPKIStatus(); 209 int statusCode = status.getStatus(); 210 if ((statusCode != PKIStatus.GRANTED) && (statusCode != PKIStatus.GRANTED_WITH_MODS)) { 211 PKIFailureInfo failureInfo = statusInfo.getPKIFailureInfo(); 212 throw new TspException("TSA reported failure:\n" + status + ((failureInfo == null) ? "" : ("\n("+failureInfo+")"))); 213 } 214 // we got a TimeStampToken 215 TimeStampToken token = response.getTimeStampToken(); 216 if (token == null) { 217 throw new TspException("Got invalid response from TSA: TimeStampToken is missing"); 218 } 219 // verify the signature of the token 220 X509Certificate tsaCert = (X509Certificate)token.getSigningCertificate(); 221 if (tsaCert == null) { 222 throw new TspException("Invalid response: does not contain the requested TSA certificate!"); 223 } 224 token.verifyTimeStampToken(tsaCert); 225 try { 226 if (token.isSigningCertificate(tsaCert) == false) { 227 throw new TspException("Certificate identified by SigningCertificate is not TSA cert!"); 228 } 229 } catch (CertificateException e) { 230 throw new TspException("Error checking SigningCertificate attribute: " + e.toString()); 231 } 232 233 // here we should validate the TSA certificate (omitted in this demo) 234 235 // get the TSTInfo 236 TSTInfo tstInfo = token.getTSTInfo(); 237 // validate the MessageImprint 238 MessageImprint mi = tstInfo.getMessageImprint(); 239 if (mi.equals(request.getMessageImprint()) == false) { 240 throw new TspException("Response MessageImprint does not match to request imprint!"); 241 } 242 // nonce included? 243 BigInteger requestNonce = request.getNonce(); 244 if (requestNonce != null) { 245 BigInteger responseNonce = tstInfo.getNonce(); 246 if (responseNonce == null) { 247 throw new TspException("Invalid Response! Does not contain nonce!"); 248 } 249 if (requestNonce.equals(responseNonce) == false) { 250 throw new TspException("Response nonce does not match to request nonce!"); 251 } 252 } 253 // did we request a TSA policy 254 ObjectID requestPolicy = request.getTSAPolicyID(); 255 if (requestPolicy != null) { 256 if (requestPolicy.equals(tstInfo.getTSAPolicyID()) == false) { 257 throw new TspException("TSA policy not trusted!"); 258 } 259 } 260 } 261 262 /** 263 * Validates an unsigned SignatureTimeStampToken contained in the given 264 * SignerInfo. 265 * 266 * @param signerInfo the SignerInfo containing the SignatureTimeStampToken attribute 267 * 268 * @throws TspException if the time stamp token validation fails 269 * @throws NullPointerException if the SignerInfo does not contain a 270 * SignatureTimeStampToken as expected 271 * 272 */ 273 public static void validateSignatureTimeStampToken(SignerInfo signerInfo) 274 throws TspException { 275 276 SignatureTimeStampToken signatureTimeStampToken; 277 TimeStampToken token; 278 try { 279 signatureTimeStampToken = 280 (SignatureTimeStampToken)signerInfo.getUnsignedAttributeValue(SignatureTimeStampToken.oid); 281 if (signatureTimeStampToken == null) { 282 throw new NullPointerException("Missing SignatureTimeStampToken in SignerInfo!"); 283 } 284 token = new TimeStampToken(signatureTimeStampToken.toASN1Object()); 285 } catch (CMSException ex) { 286 throw new TspException("Error parsing time stamp token: " + ex.toString()); 287 } catch (CodingException ex) { 288 throw new TspException("Error parsing time stamp token: " + ex.toString()); 289 } 290 // verify the signature of the token (we assume that the TSA certificate is included) 291 X509Certificate tsaCert = (X509Certificate)token.getSigningCertificate(); 292 if (tsaCert == null) { 293 throw new TspException("Cannot verify TimeStampToken: TSA certificate not included!"); 294 } 295 token.verifyTimeStampToken(tsaCert); 296 297 // here we should validate the TSA certificate (omitted in this demo) 298 299 // get the TSTInfo 300 TSTInfo tstInfo = token.getTSTInfo(); 301 302 // we may check the MessageImprint to see if actually the signature value has been time stamped 303 MessageImprint imprint = tstInfo.getMessageImprint(); 304 AlgorithmID hashAlg = imprint.getHashAlgorithm(); 305 MessageDigest md = null; 306 try { 307 md = hashAlg.getMessageDigestInstance(); 308 } catch (NoSuchAlgorithmException ex) { 309 throw new TspException("Cannot calculate MessageImprint! Hash Algorithm not supported: " + ex.getMessage()); 310 } 311 312 // calculate a hash from the signature value 313 byte[] toBeTimeStamped = md.digest(signerInfo.getSignatureValue()); 314 // and compare it against the MessageImprint value 315 if (CryptoUtils.equalsBlock(toBeTimeStamped, imprint.getHashedMessage()) == false) { 316 throw new TspException("Invalid timestamp token: wrong MessageImprint value!"); 317 } 318 319 System.out.println("Signature has been time stamped from " + tsaCert.getSubjectDN() + " at: " + tstInfo.getGenTime()); 320 } 321 322 323 324 325}