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/smime/basic/BinarySignedDemo.java 22 12.02.25 17:58 Dbratko $ 029// $Revision: 22 $ 030// 031 032package demo.smime.basic; 033 034import iaik.smime.BinaryCanonicalizer; 035import iaik.smime.DefaultCanonicalizer; 036import iaik.smime.SMimeBodyPart; 037import iaik.smime.SMimeMultipart; 038import iaik.smime.SMimeParameters; 039import iaik.smime.SignedContent; 040import iaik.x509.X509Certificate; 041 042import java.io.ByteArrayInputStream; 043import java.io.ByteArrayOutputStream; 044import java.io.IOException; 045import java.security.NoSuchAlgorithmException; 046import java.security.PrivateKey; 047import java.security.interfaces.RSAPrivateKey; 048import java.util.Date; 049 050import jakarta.activation.DataHandler; 051import jakarta.activation.FileDataSource; 052import jakarta.mail.Message; 053import jakarta.mail.MessagingException; 054import jakarta.mail.Multipart; 055import jakarta.mail.Session; 056import jakarta.mail.internet.InternetAddress; 057import jakarta.mail.internet.MimeBodyPart; 058import jakarta.mail.internet.MimeMessage; 059 060import demo.DemoSMimeUtil; 061import demo.DemoUtil; 062import demo.keystore.CMSKeyStore; 063import demo.smime.DumpMessage; 064 065/** 066 * This class shows how to create, sign and then parse/verify a 067 * mulitpart/signed message where the content is not canonicalized. 068 * <p> 069 * The only difference to the common usage of this S/MIME library 070 * is to use a {@link iaik.smime.BinaryCanonicalizer binary 071 * canonicalizer} which does not canonicalize the content. 072 * <p> 073 * To run this demo the following packages are required: 074 * <ul> 075 * <li> 076 * <code>iaik_cms.jar</code> (IAIK-CMS/SMIME) 077 * </li> 078 * <li> 079 * <code>iaik_jce(_full).jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">IAIK-JCE Core Crypto Library</a>). 080 * </li> 081 * <li> 082 * <a href="https://jakarta.ee/specifications/mail/" target="_blank">Jakarta</a>/<a href="https://eclipse-ee4j.github.io/angus-mail/" target="_blank">Angus</a> Mail 083 * </li> 084 * <li> 085 * <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a> 086 * </li> 087 * </ul> 088 * 089 * @see iaik.smime.SignedContent 090 * @see iaik.smime.BinaryCanonicalizer 091 */ 092public class BinarySignedDemo { 093 094 // whether to print dump all generates test messages to System.out 095 final static boolean PRINT_MESSAGES = false; 096 097 String firstName_ = "John"; // name of sender 098 String lastName_ = "SMime"; 099 String from_ = "smimetest@iaik.tugraz.at"; // email sender 100 String to_ = "smimetest@iaik.tugraz.at"; // email recipient 101 String host_ = "mailhost"; // name of the mailhost 102 103 X509Certificate[] signerCertificates_; // list of certificates to include in the S/MIME message 104 X509Certificate recipientCertificate_; // certificate of the recipient 105 X509Certificate signerCertificate_; // certificate of the signer/sender 106 X509Certificate encryptionCertOfSigner_; // signer uses different certificate for encryption 107 PrivateKey signerPrivateKey_; // private key of the signer/sender 108 109 /** 110 * Default constructor. Reads certificates and keys from the demo keystore. 111 */ 112 public BinarySignedDemo() { 113 114 System.out.println(); 115 System.out.println("********************************************************************************************"); 116 System.out.println("* BinarySignedDemo *"); 117 System.out.println("* (shows how to sign and verify multipart/signed S/MIME messages without canonicalization) *"); 118 System.out.println("********************************************************************************************"); 119 System.out.println(); 120 121 // get the certificates from the KeyStore 122 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 123 signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 124 signerCertificate_ = signerCertificates_[0]; 125 126 // recipient = signer for this test 127 recipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 128 encryptionCertOfSigner_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 129 } 130 131 /** 132 * Starts the demo. 133 * 134 * @throws IOException if an I/O related error occurs 135 */ 136 public void start() throws IOException { 137 138 // get the default Session 139 Session session = DemoSMimeUtil.getSession(); 140 141 142 try { 143 144 // we use a binary canonicalizer 145 SMimeParameters.setCanonicalizer(new BinaryCanonicalizer()); 146 // Create a demo Multipart 147 MimeBodyPart mbp1 = new SMimeBodyPart(); 148 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n"); 149 // attachment 150 MimeBodyPart attachment = new SMimeBodyPart(); 151 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html"))); 152 attachment.setFileName("test.html"); 153 154 Multipart mp = new SMimeMultipart(); 155 mp.addBodyPart(mbp1); 156 mp.addBodyPart(attachment); 157 DataHandler multipart = new DataHandler(mp, mp.getContentType()); 158 159 Message msg; // the message to send 160 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 161 ByteArrayInputStream bais; // we read from a stream 162 163 164 165 // create explicitly signed message 166 msg = createSignedMessage(session, multipart); 167 System.out.println("creating explicitly signed message..."); 168 baos.reset(); 169 msg.saveChanges(); 170 msg.writeTo(baos); 171 bais = new ByteArrayInputStream(baos.toByteArray()); 172 msg = new MimeMessage(session, bais); 173 if (PRINT_MESSAGES) { 174 printMessage(msg); 175 } 176 DumpMessage.dumpMsg(msg); 177 178 } catch (Exception ex) { 179 ex.printStackTrace(); 180 throw new RuntimeException(ex.toString()); 181 } finally { 182 // reset to default canonicalizer 183 SMimeParameters.setCanonicalizer(new DefaultCanonicalizer()); 184 } 185 186 } 187 188 /** 189 * Creates a signed message. 190 * 191 * @param session the mail session 192 * @param dataHandler the content of the message to be signed 193 * 194 * @return the signed message 195 * 196 * @throws MessagingException if an error occurs when creating the message 197 */ 198 public Message createSignedMessage(Session session, DataHandler dataHandler) 199 throws MessagingException { 200 201 String subject = "IAIK-S/MIME: Explicitly Signed"; 202 203 MimeMessage msg = new MimeMessage(session); 204 msg.setFrom(new InternetAddress(from_)); 205 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to_, false)); 206 msg.setSentDate(new Date()); 207 msg.setSubject(subject); 208 209 SignedContent sc = new SignedContent(false); 210 211 // set content 212 sc.setDataHandler(dataHandler); 213 sc.setCertificates(signerCertificates_); 214 215 try { 216 sc.addSigner((RSAPrivateKey)signerPrivateKey_, signerCertificate_); 217 } catch (NoSuchAlgorithmException ex) { 218 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 219 } 220 221 msg.setContent(sc, sc.getContentType()); 222 // let the SignedContent update some message headers 223 sc.setHeaders(msg); 224 return msg; 225 } 226 227 228 /** 229 * Prints a dump of the given message to System.out. 230 * 231 * @param msg the message to be dumped to System.out 232 */ 233 private static void printMessage(Message msg) throws IOException { 234 System.out.println("------------------------------------------------------------------"); 235 System.out.println("Message dump: \n"); 236 try { 237 msg.writeTo(System.out); 238 } catch (MessagingException ex) { 239 throw new IOException(ex.getMessage()); 240 } 241 System.out.println("\n------------------------------------------------------------------"); 242 } 243 244 245 /** 246 * The main method. 247 */ 248 public static void main(String[] argv) throws IOException { 249 250 DemoSMimeUtil.initDemos(); 251 (new BinarySignedDemo()).start(); 252 System.out.println("\nReady!"); 253 DemoUtil.waitKey(); 254 } 255}