001 // Copyright (C) 2002 IAIK 002 // https://jce.iaik.tugraz.at 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 // Redistribution and use in source and binary forms, with or without 011 // modification, are permitted provided that the following conditions 012 // are met: 013 // 1. Redistributions of source code must retain the above copyright 014 // notice, this list of conditions and the following disclaimer. 015 // 2. Redistributions in binary form must reproduce the above copyright 016 // notice, this list of conditions and the following disclaimer in the 017 // documentation and/or other materials provided with the distribution. 018 // 019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 020 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 021 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 023 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 024 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 025 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 027 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 028 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 029 // SUCH DAMAGE. 030 031 // Copyright (C) 2002 IAIK 032 // https://sic.tech/ 033 // 034 // Copyright (C) 2003 - 2025 Stiftung Secure Information and 035 // Communication Technologies SIC 036 // https://sic.tech/ 037 // 038 // All rights reserved. 039 // 040 // This source is provided for inspection purposes and recompilation only, 041 // unless specified differently in a contract with IAIK. This source has to 042 // be kept in strict confidence and must not be disclosed to any third party 043 // under any circumstances. Redistribution in source and binary forms, with 044 // or without modification, are <not> permitted in any case! 045 // 046 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 047 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 048 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 049 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 050 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 051 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 052 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 053 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 054 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 055 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 056 // SUCH DAMAGE. 057 // 058 // $Header: /IAIK-CMS/current/src/demo/cms/tsp/TSPServer.java 12 12.02.25 17:58 Dbratko $ 059 // 060 061 package demo.cms.tsp; 062 063 import iaik.asn1.DerCoder; 064 import iaik.asn1.ObjectID; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.tsp.PKIFailureInfo; 067 import iaik.tsp.PKIFreeText; 068 import iaik.tsp.PKIStatus; 069 import iaik.tsp.PKIStatusInfo; 070 import iaik.tsp.TSTInfo; 071 import iaik.tsp.TimeStampReq; 072 import iaik.tsp.TimeStampResp; 073 import iaik.tsp.TimeStampToken; 074 import iaik.tsp.TspSigningException; 075 import iaik.utils.LineInputStream; 076 import iaik.x509.X509Certificate; 077 078 import java.io.BufferedInputStream; 079 import java.io.BufferedOutputStream; 080 import java.io.BufferedReader; 081 import java.io.CharArrayReader; 082 import java.io.CharArrayWriter; 083 import java.io.DataOutputStream; 084 import java.io.IOException; 085 import java.io.InputStream; 086 import java.io.OutputStream; 087 import java.io.PrintWriter; 088 import java.math.BigInteger; 089 import java.net.ServerSocket; 090 import java.net.Socket; 091 import java.net.SocketException; 092 import java.security.MessageDigest; 093 import java.security.NoSuchAlgorithmException; 094 import java.security.PrivateKey; 095 import java.util.Date; 096 import java.util.StringTokenizer; 097 098 import demo.keystore.CMSKeyStore; 099 100 /** 101 * A simple TSP server. Used by the {@link TimeStampDemo TimeStampDemo}. 102 * 103 * @see TimeStampDemo 104 * 105 * @version File Revision <!-- $$Revision: --> 12 <!-- $ --> 106 */ 107 public class TSPServer { 108 109 /** 110 * Debug mode enabled? 111 */ 112 private static PrintWriter debugWriter_; 113 114 /** 115 * Carriage Return Line Feed. 116 */ 117 private static final String CRLF = "\r\n"; 118 119 /** 120 * Default Port number. 121 */ 122 private final static int DEFAULT_PORT = 3188; 123 124 /** 125 * TSA policy id. 126 */ 127 public static final ObjectID TSA_POLICY_ID = new ObjectID("1.3.6.1.4.1.2706.2.2.5.2.1.1.1", "IAIK-CMS Demo TSA"); 128 129 /** 130 * The private signing key of the tsp server. 131 */ 132 private PrivateKey privateKey_; 133 134 /** 135 * The certificate chain of the tsp server. 136 */ 137 private X509Certificate[] certChain_; 138 139 /** 140 * Algorithm to be used for signing the response. 141 */ 142 private AlgorithmID signatureAlgorithm_; 143 144 /** 145 * Algorithm to be used for calculating the signature hash. 146 */ 147 private AlgorithmID hashAlgorithm_; 148 149 /** 150 * Server socket. 151 */ 152 private ServerSocket serverSocket_; 153 154 /** 155 * Port number. 156 */ 157 private int port_; 158 159 /** 160 * Creates a TSP server for listening on time stamp 161 * requests on port 3188. 162 * Server key (for response signing) and certificate are 163 * read from the IAIK-CMS demo test keystore ("cms.keystore") 164 * which can be created by running the {@link demo.keystore.SetupCMSKeyStore 165 * SetupCMSKeyStore} program. 166 * 167 */ 168 public TSPServer() { 169 this(DEFAULT_PORT, 170 CMSKeyStore.getTspServerPrivateKey(), 171 CMSKeyStore.getTspServerCertificate(), 172 (AlgorithmID)AlgorithmID.sha256WithRSAEncryption.clone(), 173 (AlgorithmID)AlgorithmID.sha256.clone()); 174 175 176 177 } 178 179 /** 180 * Creates a TSP server. 181 * 182 * @param port the port to listen on (default 3188) 183 * @param privateKey the private key of the tsp server to be used for signing the tsp response 184 * @param certChain the certificate chain of the server (to be included in the response, if requested) 185 * @param signatureAlgorithm the algorithm used for signing the response 186 * @param hashAlgorithm algorithm to be used for calculating the signature hash 187 */ 188 public TSPServer(int port, 189 PrivateKey privateKey, 190 X509Certificate[] certChain, 191 AlgorithmID signatureAlgorithm, 192 AlgorithmID hashAlgorithm) { 193 194 if (privateKey == null) { 195 throw new NullPointerException("Private key must not be null!"); 196 } 197 if ((certChain == null) || (certChain.length == 0)) { 198 throw new NullPointerException("Certificate chain must not be null!"); 199 } 200 if (signatureAlgorithm == null) { 201 throw new NullPointerException("Signature algorithm must not be null!"); 202 } 203 if (hashAlgorithm == null) { 204 throw new NullPointerException("Hash algorithm must not be null!"); 205 } 206 port_ = (port < 0) ? DEFAULT_PORT : port; 207 privateKey_ = privateKey; 208 certChain_ = certChain; 209 signatureAlgorithm_ = signatureAlgorithm; 210 hashAlgorithm_ = hashAlgorithm; 211 } 212 213 /** 214 * Starts the TSP Server. 215 */ 216 public void start() { 217 218 if (serverSocket_ == null) { 219 try { 220 serverSocket_ = new ServerSocket(port_); 221 } catch( IOException e ) { 222 System.err.println("Error binding to port " + port_ + ":"); 223 e.printStackTrace(); 224 return; 225 } 226 } 227 debug(-1, "Listening for TSP request over HTTP on port " + port_ + "..."); 228 229 long id = 0; 230 231 // a thread for each new Request 232 while (true) { 233 if (serverSocket_ != null) { 234 try { 235 Socket socket = serverSocket_.accept(); 236 TSPServerThread tspServerThread = new TSPServerThread(socket, 237 privateKey_, 238 certChain_, 239 (AlgorithmID)signatureAlgorithm_.clone(), 240 hashAlgorithm_, 241 ++id); 242 tspServerThread.start(); 243 } catch( SocketException e ) { 244 // ignore 245 } catch( IOException e ) { 246 debug(-1, e); 247 } 248 } else { 249 break; 250 } 251 } 252 253 } 254 255 /** 256 * Stops the TSP Server. 257 */ 258 public void stop() { 259 if (serverSocket_ != null) { 260 ServerSocket serverSocket = serverSocket_; 261 serverSocket_ = null; 262 try { 263 serverSocket.close(); 264 } catch (Exception ex) { 265 // ignore 266 } 267 } 268 serverSocket_ = null; 269 } 270 271 /** 272 * Gets the port number the server is listening on. 273 */ 274 public int getPort() { 275 return port_; 276 } 277 278 /** 279 * Handles one client request. 280 */ 281 final static class TSPServerThread extends Thread { 282 283 /** 284 * The socket for talking with the client. 285 */ 286 private Socket socket_; 287 /** 288 * The private signing key of the tsp server. 289 */ 290 private PrivateKey privateKey_; 291 292 /** 293 * The certificate chain of the tsp server. 294 */ 295 private X509Certificate[] certChain_; 296 297 /** 298 * Algorithm to be used for signing the response. 299 */ 300 private AlgorithmID signatureAlgorithm_; 301 302 /** 303 * Algorithm to be used for calculating the signature hash. 304 */ 305 private AlgorithmID hashAlgorithm_; 306 307 /** 308 * The id number of the thread. 309 */ 310 private long id_; 311 312 313 /** 314 * Creates an TSP server thread for handling an TSPRequest. 315 * 316 * @param socket the socket from which to read the request and to which 317 * to send the response 318 * @param privateKey the private key of the tsp server to be used for signing the tsp response 319 * @param certChain the certificate chain of the server (to be included in the response, if requested) 320 * @param signatureAlgorithm the algorithm used for signing the response 321 * @param hashAlgorithm the algorithm used for calculating the signature hash 322 * @param id the id number of the thread 323 */ 324 public TSPServerThread(Socket socket, 325 PrivateKey privateKey, 326 X509Certificate[] certChain, 327 AlgorithmID signatureAlgorithm, 328 AlgorithmID hashAlgorithm, 329 long id) { 330 331 super("TSPServerThread" + id); 332 this.socket_ = socket; 333 privateKey_ = privateKey; 334 certChain_ = certChain; 335 signatureAlgorithm_ = signatureAlgorithm; 336 hashAlgorithm_ = hashAlgorithm; 337 id_ = id; 338 } 339 340 /** 341 * Handles the client request. 342 */ 343 public void run() { 344 try { 345 socket_.setSoTimeout(1000*30); 346 347 OutputStream os = socket_.getOutputStream(); 348 InputStream is = socket_.getInputStream(); 349 DataOutputStream out = new DataOutputStream(new BufferedOutputStream(os)); 350 LineInputStream in = new LineInputStream(new BufferedInputStream(is)); 351 352 String line; 353 line = in.readLine(); 354 StringTokenizer token = new StringTokenizer(line, " "); 355 String method = token.nextToken(); 356 debug(id_, "Received request from " + socket_.getInetAddress() + ":"); 357 debug(id_, line); 358 boolean invalidRequest = false; 359 // print header lines 360 do { 361 line = in.readLine(); 362 debug(id_, line); 363 line = line.toLowerCase(); 364 if (line.startsWith("content-type") && 365 (line.indexOf("application/timestamp-query") == -1)) { 366 invalidRequest = true; 367 } 368 } while( (line != null) && (line.length() != 0) ); 369 370 // we only accept POST 371 if (!method.equalsIgnoreCase("POST")) { 372 debug(id_, "\nInvalid method: " + method + ". Only POST supported. Sending ERROR"); 373 out.writeBytes("HTTP/1.0 405 Method Not Allowed"); 374 out.writeBytes(CRLF); 375 out.writeBytes("Content-Type: text/html"); 376 out.writeBytes(CRLF); 377 out.writeBytes("Server: IAIK TSP Demoserver"); 378 out.writeBytes(CRLF); 379 out.writeBytes(CRLF); 380 out.writeBytes("<HTML>"); 381 out.writeBytes(CRLF); 382 out.writeBytes("<HEAD><TITLE>IAIK-CMS TSP Demo Server</TITLE></HEAD>"); 383 out.writeBytes(CRLF); 384 out.writeBytes("<BODY>"); 385 out.writeBytes(CRLF); 386 out.writeBytes("<H1>405 Method Not Allowed.</H1>"); 387 out.writeBytes(CRLF); 388 out.writeBytes("<P>Method " + method + " not supported."); 389 out.writeBytes(CRLF); 390 out.writeBytes("<HR>Generated by <A HREF=\"https://sic.tech/\">IAIK-CMS</A>."); 391 out.writeBytes("</BODY>"); 392 out.writeBytes(CRLF); 393 out.writeBytes("</HTML>"); 394 out.writeBytes(CRLF); 395 out.flush(); 396 out.close(); 397 return; 398 } 399 if (invalidRequest) { 400 debug(id_, "Invalid request content type. Sending ERROR."); 401 out.writeBytes("HTTP/1.0 400 Invalid request"); 402 out.writeBytes(CRLF); 403 out.writeBytes("Content-Type: text/html"); 404 out.writeBytes(CRLF); 405 out.writeBytes("Server: IAIK TSP Demoserver"); 406 out.writeBytes(CRLF); 407 out.writeBytes(CRLF); 408 out.writeBytes("<HTML>"); 409 out.writeBytes(CRLF); 410 out.writeBytes("<HEAD><TITLE>IAIK-CMS TSP Demo Server</TITLE></HEAD>"); 411 out.writeBytes(CRLF); 412 out.writeBytes("<BODY>"); 413 out.writeBytes(CRLF); 414 out.writeBytes("<H1>400 Invalid Request.</H1>"); 415 out.writeBytes(CRLF); 416 out.writeBytes("<P>Invalid Request. Expected <code>application/timestamp-query</code>"); 417 out.writeBytes(CRLF); 418 out.writeBytes("<HR>Generated by <A HREF=\"https://sic.tech/\">IAIK-CMS</A>."); 419 out.writeBytes("</BODY>"); 420 out.writeBytes(CRLF); 421 out.writeBytes("</HTML>"); 422 out.writeBytes(CRLF); 423 out.flush(); 424 out.close(); 425 return; 426 } 427 // parse the request received 428 byte[] response = null; 429 debug(id_, "Parse request..."); 430 TimeStampResp tspResponse = createResponse(in, 431 privateKey_, 432 certChain_, 433 signatureAlgorithm_, 434 hashAlgorithm_, 435 id_); 436 response = tspResponse.getEncoded(); 437 // now create and send the response 438 debug(id_, "Sending response..."); 439 out.writeBytes("HTTP/1.0 200 OK"); 440 out.writeBytes(CRLF); 441 out.writeBytes("Content-Type: application/timestamp-reply"); 442 out.writeBytes(CRLF); 443 out.writeBytes("Server: IAIK TSP Demoserver"); 444 out.writeBytes(CRLF); 445 out.writeBytes("Content-Length: " + response.length); 446 out.writeBytes(CRLF); 447 out.writeBytes(CRLF); 448 out.write(response); 449 out.flush(); 450 out.close(); 451 } catch (IOException ex) { 452 debug(id_, ex); 453 } finally { 454 try { 455 socket_.close(); 456 } catch (IOException e) { 457 // ignore 458 } 459 } 460 } 461 } 462 463 /** 464 * Creates a TimeStampResponse from the given data 465 * 466 * @param in the input stream from which to read the time stamp request 467 * @param privateKey the private signing key of the tsp server 468 * @param certChain the certificate chain of the tsp server 469 * @param signatureAlgorithm the algorithm to be used for signing 470 * @param hashAlgorithm the algorithm to be used for signature hashing 471 * @param id an id counter (used for serial number creation) 472 * 473 * @return the time stamp response 474 */ 475 private final static TimeStampResp createResponse(InputStream in, 476 PrivateKey privateKey, 477 X509Certificate[] certChain, 478 AlgorithmID signatureAlgorithm, 479 AlgorithmID hashAlgorithm, 480 long id) { 481 482 TimeStampResp tspResponse = null; 483 // parse request 484 TimeStampReq tspRequest = null; 485 try { 486 tspRequest = new TimeStampReq(DerCoder.decode(in)); 487 // specific TSA policy requested? 488 ObjectID reqPolicy = tspRequest.getTSAPolicyID(); 489 if ((reqPolicy != null) && (reqPolicy.equals(TSA_POLICY_ID) == false)) { 490 String msg = "Requested policy " + reqPolicy.getID() + " not accepted."; 491 PKIStatusInfo statusInfo = new PKIStatusInfo(new PKIStatus(PKIStatus.REJECTION)); 492 statusInfo.setPKIFailureInfo(new PKIFailureInfo(PKIFailureInfo.UNACCEPTED_POLICY)); 493 statusInfo.setPKIFreeText(new PKIFreeText(msg)); 494 tspResponse = new TimeStampResp(statusInfo); 495 } 496 497 } catch (Exception ex) { 498 String msg = "Invalid time stamp request. "; 499 debug(id, msg + ex.toString()); 500 debug(id, ex); 501 PKIStatusInfo statusInfo = new PKIStatusInfo(new PKIStatus(PKIStatus.REJECTION)); 502 statusInfo.setPKIFailureInfo(new PKIFailureInfo(PKIFailureInfo.BAD_DATA_FORMAT)); 503 statusInfo.setPKIFreeText(new PKIFreeText(msg)); 504 tspResponse = new TimeStampResp(statusInfo); 505 } 506 507 if (tspResponse == null) { 508 // request successfully parsed 509 510 //create TSTInfo 511 TSTInfo tstInfo = new TSTInfo(); 512 tstInfo.setGenTime(new Date()); 513 tstInfo.setMessageImprint(tspRequest.getMessageImprint()); 514 if (tspRequest.getNonce() != null) { 515 tstInfo.setNonce(tspRequest.getNonce()); 516 } 517 518 // for simplicity we calculate the serial number from current date 519 // and a serial number counter. The value of a counter (id) is not kept 520 // beyound the lifetime of the server (as it might have be done practice). 521 long serialNumber = id + System.currentTimeMillis(); 522 tstInfo.setSerialNumber(BigInteger.valueOf(serialNumber)); 523 tstInfo.setTSAPolicyID(TSA_POLICY_ID); 524 525 //create TimeStampToken 526 TimeStampToken token = new TimeStampToken(tstInfo); 527 if (tspRequest.getCertReq()) { 528 token.setCertificates(certChain); 529 } 530 token.setSigningCertificate(certChain[0]); 531 532 token.setHashAlgorithm(hashAlgorithm); 533 token.setPrivateKey(privateKey); 534 535 try { 536 token.signTimeStampToken(null, signatureAlgorithm); 537 } catch (TspSigningException ex) { 538 String msg = "Error signing time stamp response. "; 539 debug(id ,msg); 540 debug(id , ex); 541 PKIStatusInfo statusInfo = new PKIStatusInfo(new PKIStatus(PKIStatus.REJECTION)); 542 statusInfo.setPKIFailureInfo(new PKIFailureInfo(PKIFailureInfo.SYSTEM_FAILURE)); 543 tspResponse = new TimeStampResp(statusInfo); 544 } 545 546 if (tspResponse == null) { 547 // successful response 548 tspResponse = new TimeStampResp(); 549 tspResponse.setTimeStampToken(token); 550 551 PKIStatus status = new PKIStatus(PKIStatus.GRANTED); 552 PKIStatusInfo info = new PKIStatusInfo(status); 553 554 tspResponse.setPKIStatusInfo(info); 555 } 556 } 557 558 return tspResponse; 559 } 560 561 /** 562 * Writes debug information to the debug stream. 563 * 564 * @param id an id to be printed in front of the message 565 * @param msg the message to be printed 566 */ 567 private final static void debug(long id, String msg) { 568 if (debugWriter_ != null) { 569 if (id < 0) { 570 debugWriter_.println("tsp_debug: " + msg); 571 } else { 572 debugWriter_.println("tsp_debug(" + id + "): " + msg); 573 } 574 } 575 } 576 577 /** 578 * Debugs an exception. 579 * 580 * @param id an id to be printed in front of the exception messages 581 * @param e the exception to be debugged 582 */ 583 private final static void debug(long id, Throwable e) { 584 CharArrayWriter writer = new CharArrayWriter(); 585 PrintWriter pwriter = new PrintWriter(writer); 586 e.printStackTrace(pwriter); 587 pwriter.flush(); 588 char[] chars = writer.toCharArray(); 589 BufferedReader reader = new BufferedReader(new CharArrayReader(chars)); 590 try { 591 while (true) { 592 String line = reader.readLine(); 593 if (line == null) { 594 break; 595 } 596 debug(id, line); 597 } 598 } catch (IOException ex) { 599 // ignore 600 } 601 } 602 603 604 605 /** 606 * Sets a stream to which debugging information shall be printed. 607 * 608 * @param os the stream to which to print debugging information 609 * or <code>null</code> if no debugging information shall 610 * be printed 611 */ 612 public static synchronized void setDebugStream(OutputStream os) { 613 debugWriter_ = (os == null) ? null : new PrintWriter(os, true); 614 } 615 616 617 618 }