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/smime/DumpMessage.java 28 12.02.25 17:58 Dbratko $ 059 // $Revision: 28 $ 060 // 061 062 package demo.smime; 063 064 import iaik.asn1.CodingException; 065 import iaik.asn1.structures.Attribute; 066 import iaik.cms.CertificateIdentifier; 067 import iaik.cms.KeyIdentifier; 068 import iaik.cms.RecipientInfo; 069 import iaik.cms.SignerInfo; 070 import iaik.pkcs.pkcs10.CertificateRequest; 071 import iaik.smime.AuthEncryptedContent; 072 import iaik.smime.CompressedContent; 073 import iaik.smime.EncryptedContent; 074 import iaik.smime.PKCS10Content; 075 import iaik.smime.SignedContent; 076 import iaik.smime.ess.Receipt; 077 import iaik.smime.ess.ReceiptContent; 078 import iaik.utils.Util; 079 import iaik.x509.X509Certificate; 080 081 import java.io.InputStream; 082 import java.security.PrivateKey; 083 import java.security.SignatureException; 084 import java.security.cert.Certificate; 085 import java.util.Date; 086 087 import javax.mail.Address; 088 import javax.mail.Flags; 089 import javax.mail.Message; 090 import javax.mail.MessagingException; 091 import javax.mail.Multipart; 092 import javax.mail.Part; 093 094 import demo.keystore.CMSKeyStore; 095 096 097 /** 098 * Simple utility for dumping a message to System.out. 099 * The parts of the message are recursively dumped. 100 * Note that this utility is not thread-safe (i.e. only 101 * one {@link #privateKey_ private key} can be statically 102 * set to decrypt an encrypted content part. 103 */ 104 public class DumpMessage { 105 106 /** 107 * Dumps the given message to System.out. 108 * 109 * @param msg the message to be dumped 110 * 111 * @throws Exception if an error occurs 112 */ 113 public static void dumpMsg(Message msg) throws Exception { 114 DumpMessage dumpMsg = new DumpMessage(); 115 dumpMsg.dump(msg); 116 } 117 118 119 /** 120 * Dumps the given encrypted message and decrypts the content for 121 * the several recipients. 122 * 123 * @param msg the message to be dumped 124 * 125 * @throws Exception if an error occurs 126 */ 127 public static void dumpEncryptedMessage(Message msg) throws Exception { 128 DumpMessage dumpMsg = new DumpMessage(); 129 dumpMsg.dumpEnvelope(msg); 130 EncryptedContent ec = (EncryptedContent)msg.getContent(); 131 if (ec instanceof AuthEncryptedContent) { 132 System.out.println("This message is authenticated encrypted!"); 133 } else { 134 System.out.println("This message is encrypted!"); 135 } 136 RecipientInfo[] recipients = ec.getRecipientInfos(); 137 for (int i = 0; i < recipients.length; i++) { 138 KeyIdentifier[] recipientKeyIds = recipients[i].getRecipientIdentifiers(); 139 for (int j = 0; j < recipientKeyIds.length; j++) { 140 KeyIdentifier recipientKeyId = recipientKeyIds[j]; 141 // in practice the message will be decrypted by one recipient only; however, 142 // since we demonstrate decryption for each recipient we must parse the message 143 // anew (otherwise the content has been already decrypted) 144 ec = (EncryptedContent)msg.getContent(); 145 dumpMsg.dumpEncrypted(ec, recipientKeyId); 146 System.out.println("############################"); 147 } 148 } 149 } 150 151 /** 152 * Private key; if set used for decrypting an encrypted entity. 153 * Otherwise the {@link demo.keystore.CMSKeyStore CMSKeyStore} used by the demos 154 * is {@link demo.keystore.CMSKeyStore CMSKeyStore#getRecipientCert(KeyIdentifier) searched} 155 * for a recipient decryption key based on the recipient key identifier. 156 */ 157 private PrivateKey privateKey_; 158 159 /** 160 * The certificates of the signer; got from a message, if it is signed. 161 */ 162 private X509Certificate[] signerCerts_; 163 164 165 /** 166 * Default constructor. 167 */ 168 public DumpMessage() { 169 } 170 171 /** 172 * Sets the private recipient key to be used to decrypt an encrypted message. 173 * <br> 174 * If not set the {@link demo.keystore.CMSKeyStore CMSKeyStore} used by the demos 175 * is {@link demo.keystore.CMSKeyStore CMSKeyStore#getRecipientCert(KeyIdentifier) searched} 176 * for a recipient decryption key based on the recipient key identifier. 177 * 178 * @param recipientKey the private key of the recipient 179 */ 180 public void setRecipientKey(PrivateKey recipientKey) { 181 privateKey_ = recipientKey; 182 } 183 184 /** 185 * Gets the certificates of the signer included in a signed message. 186 * 187 * @return the signer certificates; it the message is signed and contains the certificate 188 * of the signer; <code>null</code> otherwise. 189 */ 190 public X509Certificate[] getSignerCerts() { 191 return signerCerts_; 192 } 193 194 195 /** 196 * Dumps the given object (message, part,...) to System.out. 197 * 198 * @param o the object to be dumped 199 * 200 * @throws Exception if an error occurs 201 */ 202 public void dump(Object o) throws Exception { 203 if (o instanceof Message) { 204 dumpEnvelope((Message)o); 205 } 206 if (o instanceof Part) { 207 System.out.println("CONTENT-TYPE: "+((Part)o).getContentType()); 208 o = ((Part)o).getContent(); 209 } 210 211 if (o instanceof AuthEncryptedContent) { 212 // authenticated encrypted 213 System.out.println("This message is authenticated encrypted!"); 214 AuthEncryptedContent ec = (AuthEncryptedContent)o; 215 dumpAuthEncrypted(ec, null); 216 } else if (o instanceof EncryptedContent) { 217 // encrypted 218 System.out.println("This message is encrypted!"); 219 EncryptedContent ec = (EncryptedContent)o; 220 dumpEncrypted(ec, null); 221 } else if (o instanceof SignedContent) { 222 // signed 223 System.out.println("This message is signed!"); 224 SignedContent sc = (SignedContent)o; 225 dumpSigned(sc); 226 } else if (o instanceof PKCS10Content) { 227 System.out.println("This message contains a certificate request:"); 228 PKCS10Content pkcs10 = (PKCS10Content)o; 229 dumpPKCS10(pkcs10); 230 } else if (o instanceof CompressedContent) { 231 System.out.println("The content of this message is compressed."); 232 CompressedContent compressed = (CompressedContent)o; 233 dump(compressed.getContent()); 234 } else if (o instanceof ReceiptContent) { 235 System.out.println("This message contains a signed receipt:"); 236 ReceiptContent rc = (ReceiptContent)o; 237 dumpReceiptContent(rc); 238 } else if (o instanceof String) { 239 System.out.println("Content is a String"); 240 System.out.println("---------------------------"); 241 System.out.println((String)o); 242 } else if (o instanceof Multipart) { 243 System.out.println("----------------> Content is a Multipart"); 244 Multipart mp = (Multipart)o; 245 int count = mp.getCount(); 246 for (int i = 0; i < count; i++) { 247 System.out.println("----------------> Multipart: "+(i+1)); 248 dump(mp.getBodyPart(i)); 249 } 250 System.out.println("----------------> End of Multipart"); 251 } else if (o instanceof Message) { 252 System.out.println("Content is a Nested Message"); 253 System.out.println("---------------------------"); 254 dump(o); 255 } else if (o instanceof InputStream) { 256 System.out.println("Content is just an input stream: "+o); 257 System.out.println("---------------------------"); 258 InputStream is = (InputStream)o; 259 int a; 260 int sum = 0; 261 byte[] buf = new byte[1024]; 262 while ((a = is.read(buf)) > 0) { 263 sum += a; 264 } 265 System.out.println("Length of data: "+sum+" bytes"); 266 } 267 } 268 269 /** 270 * Dumps the given encrypted content. 271 * 272 * @param ec the EncryptedContent to be dumped 273 * @param recipientKeyId the id of the recipient key to be used for decrypting the content 274 * 275 * @throws Exception if an error occurs 276 */ 277 public void dumpEncrypted(EncryptedContent ec, KeyIdentifier recipientKeyId) throws Exception { 278 279 if (recipientKeyId == null) { 280 if (privateKey_ != null) { 281 // take the key that has been explicitly set 282 dumpEncrypted(ec, null, privateKey_); 283 } else { 284 // search key data base for a key for any of the included recipients 285 boolean foundKey = false; 286 RecipientInfo[] recipients = ec.getRecipientInfos(); 287 for (int i = 0; i < recipients.length; i++) { 288 KeyIdentifier[] recipientKeyIds = recipients[i].getRecipientIdentifiers(); 289 for (int j = 0; j < recipientKeyIds.length; j++) { 290 KeyIdentifier recipientId = recipientKeyIds[j]; 291 PrivateKey recipientKey = CMSKeyStore.getRecipientKey(recipientId); 292 if (recipientKey != null) { 293 dumpEncrypted(ec, recipientId, recipientKey); 294 foundKey = true; 295 break; 296 } 297 } 298 if (foundKey) { 299 break; 300 } 301 } 302 if (foundKey == false) { 303 throw new Exception("No recipient key found!"); 304 } 305 } 306 } else { 307 // search for the key based on the given recipient key id 308 PrivateKey recipientKey = CMSKeyStore.getRecipientKey(recipientKeyId); 309 if (recipientKey == null) { 310 throw new Exception("No key available for " + recipientKeyId); 311 } 312 dumpEncrypted(ec, recipientKeyId, recipientKey); 313 } 314 } 315 316 /** 317 * Dumps the given encrypted content for the recipient identified by the given recipient key id 318 * using the given private recipient decryption key. 319 * 320 * @param ec the EncryptedContent to be dumped 321 * @param recipientKeyId the id of the recipient key to be used for decrypting the content 322 * @param recipientKey the private key of the recipient 323 * 324 * @throws Exception if an error occurs 325 */ 326 public void dumpEncrypted(EncryptedContent ec, KeyIdentifier recipientKeyId, PrivateKey recipientKey) throws Exception { 327 System.out.println("Encryption algorithm: " + ec.getEncryptionAlgorithm().getName()); 328 if (recipientKeyId == null) { 329 // take the key that has been explicitly set 330 ec.decryptSymmetricKey(privateKey_, 0); 331 } else { 332 X509Certificate recipientCert = CMSKeyStore.getRecipientCert((CertificateIdentifier)recipientKeyId); 333 System.out.println("Decrypting message for " + recipientCert.getSubjectDN()); 334 ec.decryptSymmetricKey(recipientKey, recipientKeyId); 335 } 336 dump(ec.getContent()); 337 } 338 339 /** 340 * Dumps the given authenticated encrypted content. 341 * 342 * @param ec the AuthEncryptedContent to be dumped 343 * @param recipientKeyId the id of the recipient key to be used for decrypting the content 344 345 * 346 * @throws Exception if an error occurs 347 */ 348 public void dumpAuthEncrypted(AuthEncryptedContent ec, KeyIdentifier recipientKeyId) throws Exception { 349 dumpEncrypted(ec, recipientKeyId); 350 } 351 352 /** 353 * Dumps the given signed content. 354 * 355 * @param sc the SignedContent to be dumped 356 * 357 * @throws Exception if an error occurs 358 */ 359 public void dumpSigned(SignedContent sc) throws Exception { 360 361 if (sc.getSMimeType().equals("certs-only")) { 362 System.out.println("This message contains only certificates!"); 363 Certificate[] certs = sc.getCertificates(); 364 for (int i = 0; i < certs.length; ++i) { 365 System.out.println(certs[i].toString()); 366 } 367 } else { 368 X509Certificate signer = null; 369 try { 370 signer = sc.verify(); 371 System.out.println("This message is signed from: "+signer.getSubjectDN()); 372 } catch (SignatureException ex) { 373 throw new SignatureException("Signature verification error: " + ex.toString()); 374 } 375 376 SignerInfo[] signerInfos = sc.getSignerInfos(); 377 for (int i=0; i<signerInfos.length; i++) { 378 System.out.println("Digest algorithm: " + signerInfos[i].getDigestAlgorithm().getName()); 379 System.out.println("Signature algorithm: " + signerInfos[i].getSignatureAlgorithm().getName()); 380 Attribute[] signedAttributes = signerInfos[i].getSignedAttributes(); 381 if ((signedAttributes != null) && (signedAttributes.length > 0)) { 382 System.out.println("SignerInfo " + i + " contains the following signed attributes:"); 383 for (int j = 0; j < signedAttributes.length; j++) { 384 dumpAttribute(signedAttributes[j]); 385 } 386 } 387 Attribute[] unsignedAttributes = signerInfos[i].getUnsignedAttributes(); 388 if ((unsignedAttributes != null) && (unsignedAttributes.length > 0)) { 389 System.out.println("SignerInfo " + i + " contains the following unsigned attributes:"); 390 for (int j = 0; j < unsignedAttributes.length; j++) { 391 dumpAttribute(unsignedAttributes[j]); 392 } 393 } 394 } 395 if (!signerInfos[0].isSignerCertificate(signer)) { 396 System.out.println("Signer certificate check failed!"); 397 } 398 signerCerts_ = Util.convertCertificateChain(sc.getCertificates()); 399 // arrange chain to get user cert at index 0 400 signerCerts_ = Util.createCertificateChain(signer, signerCerts_); 401 dump(sc.getContent()); 402 } 403 } 404 405 /** 406 * Dumps the given PKCS#10 content. 407 * 408 * @param pkcs10 the PKCS10Content to be dumped 409 * 410 * @throws Exception if an error occurs 411 */ 412 public void dumpPKCS10(PKCS10Content pkcs10) throws Exception { 413 CertificateRequest request = pkcs10.getCertRequest(); 414 System.out.println(request.toString()); 415 try { 416 if (request.verify()) { 417 System.out.println("Request verification ok for " + request.getSubject()); 418 } else { 419 throw new SignatureException("Incorrect signature!"); 420 } 421 } catch (SignatureException ex) { 422 throw new SignatureException("Request verification error for " + request.getSubject() + ex.getMessage()); 423 } 424 } 425 426 /** 427 * Dumps the given signed receipt. 428 * 429 * @param rc the signed receipt to be dumped 430 * 431 * @throws Exception if an error occurs 432 */ 433 public void dumpReceiptContent(ReceiptContent rc) throws Exception { 434 435 Receipt receipt = (Receipt)rc.getContent(); 436 System.out.println(receipt); 437 // verify the signature (we assume only one signer) 438 X509Certificate receiptSigner = null; 439 try { 440 receiptSigner = rc.verify(); 441 System.out.println("This receipt content is signed from: "+receiptSigner.getSubjectDN()); 442 } catch (SignatureException ex) { 443 System.err.println("Signature verification error!"); 444 throw ex; 445 } 446 } 447 448 449 /** 450 * Prints the envelope of a message. 451 * 452 * @param m the message 453 * 454 * @throws MessagingException if an error occurs 455 */ 456 public void dumpEnvelope(Message m) throws MessagingException { 457 System.out.println("This is the message envelope"); 458 System.out.println("---------------------------"); 459 Address[] a; 460 // FROM 461 if ((a = m.getFrom()) != null) { 462 for (int j = 0; j < a.length; j++) 463 System.out.println("FROM: " + a[j].toString()); 464 } 465 466 // TO 467 if ((a = m.getRecipients(Message.RecipientType.TO)) != null) { 468 for (int j = 0; j < a.length; j++) 469 System.out.println("TO: " + a[j].toString()); 470 } 471 472 // SUBJECT 473 System.out.println("SUBJECT: " + m.getSubject()); 474 475 // DATE 476 Date d = m.getSentDate(); 477 System.out.println("SendDate: "+(d != null ? d.toString() : "UNKNOWN")); 478 479 // SIZE 480 System.out.println("Size: " + m.getSize()); 481 482 // FLAGS: 483 Flags flags = m.getFlags(); 484 StringBuffer sb = new StringBuffer(); 485 Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags 486 487 boolean first = true; 488 for (int i = 0; i < sf.length; i++) { 489 String s; 490 Flags.Flag f = sf[i]; 491 if (f == Flags.Flag.ANSWERED) 492 s = "\\Answered"; 493 else if (f == Flags.Flag.DELETED) 494 s = "\\Deleted"; 495 else if (f == Flags.Flag.DRAFT) 496 s = "\\Draft"; 497 else if (f == Flags.Flag.FLAGGED) 498 s = "\\Flagged"; 499 else if (f == Flags.Flag.RECENT) 500 s = "\\Recent"; 501 else if (f == Flags.Flag.SEEN) 502 s = "\\Seen"; 503 else 504 continue; // skip it 505 if (first) 506 first = false; 507 else 508 sb.append(' '); 509 sb.append(s); 510 } 511 512 String[] uf = flags.getUserFlags(); // get the user flag strings 513 for (int i = 0; i < uf.length; i++) { 514 if (first) 515 first = false; 516 else 517 sb.append(' '); 518 sb.append(uf[i]); 519 } 520 System.out.println("FLAGS = " + sb.toString()); 521 522 // X-MAILER 523 String[] hdrs = m.getHeader("X-Mailer"); 524 if (hdrs != null) 525 System.out.println("X-Mailer: " + hdrs[0]); 526 else 527 System.out.println("X-Mailer NOT available"); 528 } 529 530 /** 531 * Dumps the given attribute to System.out. 532 * 533 * @param attribute the Attribute 534 * 535 * @throws CodingException if the attribute cannot be parsed 536 */ 537 private static void dumpAttribute(Attribute attribute) throws CodingException { 538 System.out.println(attribute.toString() + "\n"); 539 } 540 } 541