/* * [Verify.java] * * Summary: Verify the digital signature on a message. * * Copyright: (c) 2004-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.4 2006-03-05 reformat with IntelliJ and add Javadoc. */ package com.mindprod.transporter; import java.io.IOException; import java.io.Serializable; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Verify the digital signature on a message. *

* Using compression and base64u armouring. *

* No encryption. See Transporter.java for example of use. * * @author Roedy Green, Canadian Mind Products * @version 1.4 2006-03-05 reformat with IntelliJ and add Javadoc. * @since 2004-06-08 */ public final class Verify extends Unwrap { /** * true if debugging */ private static final boolean DEBUGGING = false; /** * the sender's public key */ protected final PublicKey senderPublicKey; /** * Constructor. Set public key in preparation for verifying signature. * * @param senderPublicKey sender's public key */ public Verify( PublicKey senderPublicKey ) { super(); this.senderPublicKey = senderPublicKey; } /** * verify signature on message and remove it * * @param signedMessage digitally signed message * * @return message with signature stripped. * @throws IOException if fails to verify. */ protected byte[] verify( byte[] signedMessage ) throws IOException { // format of a signed message: // ( 16-bits signature length) ( 32-bits encrypted salt, encrypted // digest ) ( unencrypted message all in one block ) // encrypted with sender's private key. try { if ( DEBUGGING ) { Tools.dumpBytes( "signedMessage", signedMessage ); } /* * Message has length byte, encrypted signature, message . Split * message into three pieces. */ int encryptedDigestLength = Tools.getBlockLength( signedMessage, 0 ); int unsignedMessageLength = signedMessage.length - encryptedDigestLength - 2; byte[] encryptedDigest = Tools.subarray( signedMessage, 2, encryptedDigestLength ); if ( DEBUGGING ) { Tools.dumpBytes( "encryptedDigest", encryptedDigest ); } byte[] unsignedMessage = Tools.subarray( signedMessage, encryptedDigestLength + 2, unsignedMessageLength ); if ( DEBUGGING ) { Tools.dumpBytes( "unsignedMessage", unsignedMessage ); } byte[] expectedDigest = Tools.unsalt( Tools.processWithPublicKey( encryptedDigest, senderPublicKey ) ); if ( DEBUGGING ) { Tools.dumpBytes( "expectedDigest", expectedDigest ); } MessageDigest md = MessageDigest.getInstance( "MD5" ); md.update( unsignedMessage ); byte[] actualDigest = md.digest(); if ( DEBUGGING ) { Tools.dumpBytes( "actualDigest", actualDigest ); } if ( !Tools.equals( expectedDigest, actualDigest ) ) { throw new IOException( "Digital signature not valid" ); } if ( DEBUGGING ) { Tools.dumpBytes( "unsignedMessage", unsignedMessage ); } return unsignedMessage; } catch ( NoSuchAlgorithmException e ) { throw new IOException( "Java missing the MD5 class. Try version 1.4+ " + e.getMessage() ); } catch ( ArrayIndexOutOfBoundsException e ) { throw new IOException( "garbled message" ); } } /** * unwraps an armoured, compressed, serialised object, and verifies its signature. does not parse parm=value. * * @param s base64u string. * * @return reconstituted object. * @throws IOException if signature fails to verify. */ public Serializable unwrap( String s ) throws IOException { return reconstitute( decompress( verify( disarmour( s ) ) ) ); } }