diff --git a/.gitignore b/.gitignore index 201af53c..8dad7d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ pom.xml.versionsBackup /Bundle/target/ /lib/ /Server/src/logs/ +**/.DS_Store +**/.settings +**/*.class +.metadata/* \ No newline at end of file diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 31e290cd..097a2bce 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,20 +1,25 @@ # OpenAS2 Server -# Version 3.2.1 +# Version 3.3.0 # RELEASE NOTES ----- -The OpenAS2 project is pleased to announce the release of OpenAS2 3.2.1 +The OpenAS2 project is pleased to announce the release of OpenAS2 3.3.0 -The release download file is: OpenAS2Server-3.2.1.zip +The release download file is: OpenAS2Server-3.3.0.zip The zip file contains a PDF document (OpenAS2HowTo.pdf) providing information on installing and using the application. ## NOTE: Testing covers Java 8 to 17. The application should work for older versions down to Java 7 but they are not tested as part of the CI/CD pipeline. -Version 3.2.1 - 2022-07-06 -This is a minor enhancement and bugfix release: +Version 3.3.0 - 2022-08-13 +This is a significant enhancement and minor bugfix release: **IMPORTANT NOTE**: Please review upgrade notes below if you are upgrading - 1. Fix extracting the MDN bodypart from the multipart. - 2. Fix creating a zip bomb package. + 1. Support rejecting messages being sent that are unsigned. See section "Reject Unsigned Messages" in the documentation. + 2. Support having 2 certificates for your partner definition. This allows switch over of your own certificate to be zero down time. See section "Overlapping Old And New Certificates When Changing" in the documentation. + 3. Support overriding most of the config.xml attribute values using the external property file. This allows custom configuration to be restricted to a single properties file and makes version upgrades much simpler. + 4. Enhance shell and bat scripts to prepare for install and upgrade scripts to simplify managing the install and upgrade of OpenAS2 in the future. + 5. Enhance the properties parser to support periods in the property name. + 6. Fix the erroneous error when cleaning up files due to a duplicate call to the file cleanup function. + ##Upgrade Notes diff --git a/Remote/pom.xml b/Remote/pom.xml index 56195a8a..4270a97f 100644 --- a/Remote/pom.xml +++ b/Remote/pom.xml @@ -4,7 +4,7 @@ net.sf.openas2 OpenAS2 - 3.2.1 + 3.3.0 4.0.0 diff --git a/Server/pom.xml b/Server/pom.xml index a0c196d8..c9f814e3 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -7,7 +7,7 @@ net.sf.openas2 OpenAS2 - 3.2.1 + 3.3.0 ../pom.xml @@ -158,15 +158,15 @@ org.bouncycastle - bcmail-jdk15on + bcmail-jdk18on org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.apache.commons @@ -190,7 +190,7 @@ org.bouncycastle - bcpg-jdk15on + bcpg-jdk18on org.apache.httpcomponents diff --git a/Server/src/bin/gen_p12_key_par.sh b/Server/src/bin/gen_p12_key_par.sh index edcf41ba..9cb44920 100755 --- a/Server/src/bin/gen_p12_key_par.sh +++ b/Server/src/bin/gen_p12_key_par.sh @@ -21,6 +21,9 @@ if test $# -ne 4; then echo " >$0 as2_certs partnera SHA256 \"CN=as2.partnerb.com, OU=QA, O=PartnerA, L=New York, S=New York, C=US\"" echo " Expected OUTPUT: as2_certs.p12 - keystore containing both public and private key" echo " partnera.cer - public key certificate file ." + echo "" + echo "To run the script without prompts, set environment variables IS_AUTOMATED_EXEC=1 and KEYSTORE_PASSWORD to the desired password (can be blank)" + echo "" exit 1 fi @@ -60,15 +63,19 @@ if [ -z $JAVA_HOME ]; then exit fi -echo "Generate a certificate to a PKCS12 key store." -echo "Generating certificate: using alias $certAlias to ${tgtStore}.p12 $PRE_GEN_MSG_ADDITIONAL" -read -p "Do you wish to execute this request? [Y/N]" Response -if [ $Response != "Y" -a $Response != "y" ] ; then - exit 1 +if [ "1" != "$IS_AUTOMATED_EXEC" ]; then + echo "Generate a certificate to a PKCS12 key store." + echo "Generating certificate: using alias $certAlias to ${tgtStore}.p12 $PRE_GEN_MSG_ADDITIONAL" + read -p "Do you wish to execute this request? [Y/N]" Response + if [ $Response != "Y" -a $Response != "y" ] ; then + exit 1 + fi + read -p "Enter password for keystore:" ksPwd +else + ksPwd=$KEYSTORE_PASSWORD fi -read -p "Enter password for keystore:" ksPwd -$JAVA_HOME/bin/keytool -genkeypair -alias $certAlias -validity $CertValidDays -keyalg RSA -sigalg $sigAlg -keystore ${tgtStore}.p12 -storepass $ksPwd -storetype pkcs12 $AdditionalGenArgs -dname "$dName" +$JAVA_HOME/bin/keytool -genkeypair -alias $certAlias -validity $CertValidDays -keyalg RSA -sigalg $sigAlg -keystore ${tgtStore}.p12 -storepass "$ksPwd" -storetype pkcs12 $AdditionalGenArgs -dname "$dName" if [ "$?" != 0 ]; then echo "" echo "Failed to create a keystore. See errors above to correct the problem." @@ -76,14 +83,14 @@ if [ "$?" != 0 ]; then fi #$JAVA_HOME/bin/keytool -selfcert -alias $certAlias -validity $CertValidDays -sigalg $sigAlg -keystore ${tgtStore}.p12 -storepass $ksPwd -storetype pkcs12 -$JAVA_HOME/bin/keytool -selfcert -alias $certAlias $AdditionalGenArgs -validity $CertValidDays -sigalg $sigAlg -keystore ${tgtStore}.p12 -storepass $ksPwd -storetype pkcs12 +$JAVA_HOME/bin/keytool -selfcert -alias $certAlias $AdditionalGenArgs -validity $CertValidDays -sigalg $sigAlg -keystore ${tgtStore}.p12 -storepass "$ksPwd" -storetype pkcs12 if [ "$?" != 0 ]; then echo "" echo "Failed to self certifiy the certificates in the keystore. See errors above to correct the problem." exit 1 fi -$JAVA_HOME/bin/keytool -export -rfc -file $certAlias.cer -alias $certAlias -keystore ${tgtStore}.p12 -storepass $ksPwd -storetype pkcs12 +$JAVA_HOME/bin/keytool -export -rfc -file $certAlias.cer -alias $certAlias -keystore ${tgtStore}.p12 -storepass "$ksPwd" -storetype pkcs12 if [ "$?" != 0 ]; then echo "" echo "Failed to export the public key. See errors above to correct the problem." diff --git a/Server/src/bin/import_public_cert.bat b/Server/src/bin/import_public_cert.bat index 81b59142..dea97450 100755 --- a/Server/src/bin/import_public_cert.bat +++ b/Server/src/bin/import_public_cert.bat @@ -81,7 +81,7 @@ echo. echo Sucessfully Imported certificate from file "%srcFile%" using alias "%certAlias%" to: %tgtStore% echo. -goto :END +goto END :Usage echo Import a public certificate to a PKCS12 key store. diff --git a/Server/src/bin/install_winsvc.bat b/Server/src/bin/install_winsvc.bat old mode 100644 new mode 100755 index effb6c29..02752198 --- a/Server/src/bin/install_winsvc.bat +++ b/Server/src/bin/install_winsvc.bat @@ -1,10 +1,17 @@ +@echo off +setLocal EnableDelayedExpansion +if /I "!IS_AUTOMATED_EXEC!" == "1" goto CheckOk goto CheckRun :CheckOk REM Set the key config strings - +if /I not "!SERVICE_NAME!" == "" goto ServiceNameSet set SERVICE_NAME=OpenAS2Server +echo No override for service name specified. Using default service name... + +:ServiceNameSet +echo Using service name: !SERVICE_NAME! SET tmppath=%~dp0 pushd %tmppath% cd .. @@ -15,7 +22,8 @@ REM If the directory structure was changed from the OpenAS2 standard set path di REM set OPENAS2_BASE_DIR=c:\opt\OpenAS2 set APACHE_COMMONS_DAEMON=%OPENAS2_BASE_DIR%\bin\commons-daemon set PR_INSTALL=%APACHE_COMMONS_DAEMON%\amd64\prunsrv.exe -set config_file=%OPENAS2_BASE_DIR%\config\config.xml +set STARTUP_ARGS=%OPENAS2_BASE_DIR%\config\config.xml +set CUSTOM_SERVICE_PARAMS= set PR_CLASSPATH=%OPENAS2_BASE_DIR%\lib\* REM If using a specific JVM then uncomment & set JAVA_HOME below REM set JAVA_HOME=C:\Program Files\Java\jre1.8.0_171 @@ -36,16 +44,24 @@ set PR_LOGLEVEL=Error REM Path to java installation REM If the auto mode does not work then you can explicitly set the path to the Java install DLL set PR_JVM=auto -rem set PR_JVM=%JAVA_HOME%\bin\server\jvm.dll - +if /I "!CUSTOM_JAVA_HOME!" == "" goto SkipCustomJava +set PR_JVM=%CUSTOM_JAVA_HOME%\bin\server\jvm.dll +:SkipCustomJava + +SET PR_JVM_OPTS="-Dorg.apache.commons.logging.Log=org.openas2.logging.Log" +if /I "!OPENAS2_PROPERTIES_FILE!" == "" goto SkipArgsAdd +rem Add the property arg to JVM options +echo Setting custom properties file for service startup: !OPENAS2_PROPERTIES_FILE! +set PR_JVM_OPTS=%PR_JVM_OPTS% ++JvmOptions="-Dopenas2.properties.file=%OPENAS2_PROPERTIES_FILE%" +:SkipArgsAdd +setLocal DisableDelayedExpansion + REM Startup configuration set PR_STARTUP=auto set PR_STARTMODE=jvm set PR_STARTCLASS=org.openas2.app.OpenAS2WindowsService set PR_STARTMETHOD=start -REM 1 way to add multiple params for some systems where it seems the StartMethod does not work -REM set PR_STARTPARAMS=start ++StartParams=%config_file% -set PR_STARTPARAMS=%config_file% +set PR_STARTPARAMS=%STARTUP_ARGS% REM Shutdown configuration set PR_STOPMODE=jvm @@ -55,7 +71,11 @@ set PR_STOPPARAMS=stop REM Add the below line into the install command if using a specific JVM REM --JavaHome="%JAVA_HOME%" ^ - +if /I "!CUSTOM_JAVA_HOME!" == "" goto SkipCustomJavaHome +rem Add the property arg to JVM options +echo Setting custom properties file for service startup: !OPENAS2_PROPERTIES_FILE! +set CUSTOM_SERVICE_PARAMS=%CUSTOM_SERVICE_PARAMS% ++JavaHome="%CUSTOM_JAVA_HOME%" +:SkipCustomJavaHome REM Make the folder accessible to the "Local Service" user running the servioce icacls "%OPENAS2_BASE_DIR%" /grant *S-1-5-19:(OI)(CI)(M) @@ -73,7 +93,7 @@ REM Install service --JvmMs="%PR_JVMMS%" ^ --JvmMx="%PR_JVMMX%" ^ --JvmSs="%PR_JVMSS%" ^ - --JvmOptions="-Dorg.apache.commons.logging.Log=org.openas2.logging.Log" ^ + --JvmOptions=%PR_JVM_OPTS% ^ --Classpath="%PR_CLASSPATH%" ^ --StartMode="%PR_STARTMODE%" ^ --StartMethod="%PR_STARTMETHOD%" ^ @@ -81,7 +101,7 @@ REM Install service --StartParams=%PR_STARTPARAMS% ^ --StopMode="%PR_STOPMODE%" ^ --StopClass="%PR_STOPCLASS%" ^ - --StopParams="stop" + --StopParams="stop" %CUSTOM_SERVICE_PARAMS% goto END diff --git a/Server/src/bin/start-openas2.bat b/Server/src/bin/start-openas2.bat index 06b34fd7..86a016db 100755 --- a/Server/src/bin/start-openas2.bat +++ b/Server/src/bin/start-openas2.bat @@ -20,6 +20,9 @@ rem set EXTRA_PARMS=%EXTRA_PARMS% -Dsun.net.http.allowRestrictedHeaders=true rem When using old (unsecure) certificates (please replace them!) that fail to load from the certificate store. rem set EXTRA_PARMS=%EXTRA_PARMS% -Dorg.bouncycastle.asn1.allow_unsafe_integer=true +if [%OPENAS2_PROPERTIES_FILE%]==[]] goto skip_properties_file +set EXTRA_PARMS=%EXTRA_PARMS% -Dopenas2.properties.file="%OPENAS2_PROPERTIES_FILE%" +:skip_properties_file rem set EXTRA_PARMS=%EXTRA_PARMS% -Dhttps.protocols=TLSv1.2 rem Uncomment any of the following for enhanced debug @@ -44,7 +47,7 @@ set LIB_JARS=".!LIB_JARS!" setLocal disableDelayedExpansion rem Include the bin dir so that commons-logging.properties is found set CLASSPATH=.;%LIB_JARS%;%OPENAS2_BASE_DIR%/bin -rem echo Running: "%JAVA%" %EXTRA_PARMS% -cp %CLASSPATH% org.openas2.app.OpenAS2Server "%OPENAS2_BASE_DIR%/config/config.xml" +rem echo Running: "%JAVA%" %EXTRA_PARMS% -cp .;%LIB_JARS% org.openas2.app.OpenAS2Server "%OPENAS2_BASE_DIR%/config/config.xml" "%JAVA%" %EXTRA_PARMS% -cp .;%LIB_JARS% org.openas2.app.OpenAS2Server "%OPENAS2_BASE_DIR%/config/config.xml" :warn diff --git a/Server/src/bin/start-openas2.sh b/Server/src/bin/start-openas2.sh index 84365373..771cd503 100755 --- a/Server/src/bin/start-openas2.sh +++ b/Server/src/bin/start-openas2.sh @@ -30,6 +30,11 @@ if [ -z $OPENAS2_CONFIG_FILE ]; then fi EXTRA_PARMS="$EXTRA_PARMS -Dopenas2.config.file=${OPENAS2_CONFIG_FILE}" +# Set the properties file if set to a valid file +if [ ! -z "$OPENAS2_PROPERTIES_FILE" ] && [ -f $OPENAS2_PROPERTIES_FILE ]; then + EXTRA_PARMS="$EXTRA_PARMS -Dopenas2.properties.file=${OPENAS2_PROPERTIES_FILE}" +fi + # For versions of Java that prevent restricted HTTP headers (see documentation for discussion on this) #EXTRA_PARMS="$EXTRA_PARMS -Dsun.net.http.allowRestrictedHeaders=true" @@ -65,7 +70,7 @@ if [ -z $JAVA_HOME ]; then fi # Expand the classpath instead of using file globbing expansion in the java command as it seems to mess with Mailcap loading CLASSPATH=$(echo "${binDir}/../lib/"*".jar" | tr ' ' ':') -# Include the bin dir so that icommons-logging.properties is always found +# Include the bin dir so that commons-logging.properties is always found CLASSPATH=${CLASSPATH}:${binDir} CMD=$(echo "${JAVA_HOME}/bin/java ${PWD_OVERRIDE} ${EXTRA_PARMS} -cp .:${CLASSPATH} org.openas2.app.OpenAS2Server") echo diff --git a/Server/src/config/config.xml b/Server/src/config/config.xml index 85c3337c..64c02b38 100644 --- a/Server/src/config/config.xml +++ b/Server/src/config/config.xml @@ -4,8 +4,6 @@ log_date_format="yyyy-MM-dd HH:mm:ss.SSS" sql_timestamp_format="yyyy-MM-dd HH:mm:ss.SSS" as2_message_id_format="OPENAS2-$date.ddMMyyyyHHmmssZ$-$rand.UUID$@$msg.sender.as2_id$_$msg.receiver.as2_id$" - async_mdn_receiver_port="10081" - as2_async_mdn_url="http://localhost:$properties.async_mdn_receiver_port$" as2_receive_message_filename_fallback="$rand.shortUUID$" console.logger.enabled="true" file.logger.enabled="true" @@ -21,14 +19,50 @@ module.MessageFileModule.enabled="true" module.DirectoryResenderModule.enabled="true" module.AS2ReceiverModule.http.enabled="true" + module.AS2ReceiverModule.http.port="10080" module.AS2MDNReceiverModule.http.enabled="true" + module.AS2MDNReceiverModule.http.port="10081" module.AS2ReceiverModule.https.enabled="false" + module.AS2ReceiverModule.https.port="10443" module.AS2MDNReceiverModule.https.enabled="false" + module.AS2MDNReceiverModule.https.port="10444" module.HealthCheckModule.enabled="false" - /> + async_mdn_receiver_port="$properties.module.AS2MDNReceiverModule.http.port$" + as2_async_mdn_url="http://localhost:$properties.async_mdn_receiver_port$" + as2_keystore="%home%/as2_certs.p12" + as2_keystore_password="testas2" + ssl_keystore="%home%/ssl_certs.jks" + ssl_keystore_password="testas2" + partnership_file="%home%/partnerships.xml" + javax.mail.properties.file="%home%/java.mail.properties" + email.from="Open AS2 Server<as2msgs@openas2.org>" + email.to="your email address" + email.smtpserver="mail.openas2.org" + email.smtpport="23" + email.smtpauth="true" + email.smtpuser="mySmtpUserId" + email.smtppwd="mySmtpPwd" + email.subject="$exception.name$: $exception.message$" + email.bodytemplate="%home%/emailtemplate.txt" + file.logger.filename="%home%/../logs/log-$date.yyyyMMdd$.txt" + msg_tracking.use_embedded_db="true" + msg_tracking.force_load_jdbc_driver="false" + msg_tracking.db_user="sa" + msg_tracking.db_pwd="OpenAS2" + msg_tracking.db_name="openas2" + msg_tracking.table_name="msg_metadata" + msg_tracking.db_directory="%home%/DB" + msg_tracking.jdbc_driver="org.h2.Driver" + msg_tracking.jdbc_connect_string="jdbc:h2:$component.db_directory$/$component.db_name$" + msg_tracking.sql_escape_character="'" + msg_tracking.tcp_server_start="true" + msg_tracking.tcp_server_port="9092" + msg_tracking.tcp_server_password="openas2" + reject_unsigned_messages="false" + /> + javax.mail.properties.file="$properties.javax.mail.properties.file$" + from="$properties.email.from$" + to="$properties.email.to$" + smtpserver="$properties.email.smtpserver$" + smtpport="$properties.email.smtpport$" + smtpauth="$properties.email.auth$" + smtpuser="$properties.email.smtpuser$" + smtppwd="$properties.email.smtppwd$" + subject="$properties.email.subject$" + bodytemplate="$properties.email.bodytemplate$"/> + filename="$properties.file.logger.filename$"/> @@ -97,7 +131,7 @@ format="sender.as2_id, receiver.as2_id, attributes.filename" mimetype="application/EDI-X12"/> --> - + - - + use_embedded_db="$properties.msg_tracking.use_embedded_db$" + force_load_jdbc_driver="$properties.msg_tracking.force_load_jdbc_driver$" + db_user="$properties.msg_tracking.db_user$" + db_pwd="$properties.msg_tracking.db_pwd$" + db_name="$properties.msg_tracking.db_name$" + table_name="$properties.msg_tracking.table_name$" + db_directory="$properties.msg_tracking.db_directory$" + jdbc_driver="$properties.msg_tracking.jdbc_driver$" + jdbc_connect_string="$properties.msg_tracking.jdbc_connect_string$" + sql_escape_character="$properties.msg_tracking.sql_escape_character$" + tcp_server_start="$properties.msg_tracking.tcp_server_start$" + tcp_server_port="$properties.msg_tracking.tcp_server_port$" + tcp_server_password="$properties.msg_tracking.tcp_server_password$"/> + ssl_keystore="$properties.ssl_keystore$" + ssl_keystore_password="$properties.ssl_keystore_password$"/> entry : properties.entrySet()) { String val = entry.getValue(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Parsing property: " + entry.getKey() + " : " + val); + } String parsedVal = ParameterParser.parse(val, parser); // Parser will return empty string if there is an unmatched parser ID in the string if (parsedVal.length() > 0 && !val.equals(parsedVal)) { diff --git a/Server/src/main/java/org/openas2/message/BaseMessage.java b/Server/src/main/java/org/openas2/message/BaseMessage.java index acdc2eee..5ddb71ff 100644 --- a/Server/src/main/java/org/openas2/message/BaseMessage.java +++ b/Server/src/main/java/org/openas2/message/BaseMessage.java @@ -27,6 +27,7 @@ public abstract class BaseMessage implements Message { + /** * */ @@ -40,12 +41,15 @@ public abstract class BaseMessage implements Message { private String compressionType = ICryptoHelper.COMPRESSION_NONE; private boolean rxdMsgWasSigned = false; private boolean rxdMsgWasEncrypted = false; + private boolean fileCleanupCompleted = false; private Map options = new HashMap(); private String calculatedMIC = null; private String logMsg = null; private String status = MSG_STATUS_MSG_INIT; private Map customOuterMimeHeaders = new HashMap(); private String payloadFilename = null; + private String senderX509Alias; + private String receiverX509Alias; public BaseMessage() { @@ -56,6 +60,33 @@ public String getAppTitle() { return Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server"); } + public String getSenderX509Alias() { + return senderX509Alias; + } + + public void setSenderX509Alias(String alias) { + senderX509Alias = alias; + } + + public String getReceiverX509Alias() { + return receiverX509Alias; + } + + public void setReceiverX509Alias(String alias) { + receiverX509Alias = alias; + + } + + @Override + public boolean isFileCleanupCompleted() { + return fileCleanupCompleted; + } + + @Override + public void setFileCleanupCompleted(boolean cleanupDone) { + fileCleanupCompleted = cleanupDone; + } + public Map getOptions() { if (options == null) { options = new HashMap(); diff --git a/Server/src/main/java/org/openas2/message/Message.java b/Server/src/main/java/org/openas2/message/Message.java index a4989bde..ee81f017 100644 --- a/Server/src/main/java/org/openas2/message/Message.java +++ b/Server/src/main/java/org/openas2/message/Message.java @@ -157,6 +157,10 @@ public interface Message extends Serializable { boolean isConfiguredForAsynchMDN(); + boolean isFileCleanupCompleted(); + + void setFileCleanupCompleted(boolean cleanupDone); + String getSubject(); void setSubject(String subject); @@ -181,4 +185,11 @@ public interface Message extends Serializable { String getAppTitle(); + String getSenderX509Alias(); + + void setSenderX509Alias(String alias); + + String getReceiverX509Alias(); + + void setReceiverX509Alias(String alias); } diff --git a/Server/src/main/java/org/openas2/params/CompositeParameters.java b/Server/src/main/java/org/openas2/params/CompositeParameters.java index 1642acd7..9a242739 100644 --- a/Server/src/main/java/org/openas2/params/CompositeParameters.java +++ b/Server/src/main/java/org/openas2/params/CompositeParameters.java @@ -64,7 +64,8 @@ public String getParameter(String key) throws InvalidParameterException { String parserID = keyParts.nextToken(); // support "properties" key for all parser calls if ("properties".equals(parserID)) { - String propKey = keyParts.nextToken(); + // The property value could be a period separated string so get the original and drop "properties." + String propKey = key.replace("properties.", ""); if (propKey == null) { throw new InvalidParameterException("Invalid property key format. Missing a property name.", this, key, null); } diff --git a/Server/src/main/java/org/openas2/partner/Partnership.java b/Server/src/main/java/org/openas2/partner/Partnership.java index a8bf0038..2918711a 100644 --- a/Server/src/main/java/org/openas2/partner/Partnership.java +++ b/Server/src/main/java/org/openas2/partner/Partnership.java @@ -28,8 +28,10 @@ public class Partnership implements Serializable { public static final String PCFG_RECEIVER = PTYPE_RECEIVER; // Receiver config node /* partner definition attributes */ + public static final String PID_NAME = "name"; // Partner name public static final String PID_AS2 = "as2_id"; // AS2 ID public static final String PID_X509_ALIAS = "x509_alias"; // Alias to an X509 Certificate + public static final String PID_X509_ALIAS_FALLBACK = "x509_alias_fallback"; // Fallback alias to an X509 Certificate public static final String PID_EMAIL = "email"; // Email address /* partnership definition attributes */ @@ -58,6 +60,7 @@ public class Partnership implements Serializable { public static final String PA_HTTP_NO_CHUNKED_MAX_SIZE = "no_chunked_max_size"; // Disables chunked HTTP transfer when file size is set larger than the value in this param public static final String PA_HTTP_PREVENT_CHUNKING = "prevent_chunking"; // Will try to force the send without using chunked HTTP transfer public static final String PA_STORE_RECEIVED_FILE_TO = "store_received_file_to"; // Allows overriding the MessageFileModule "filename" parameter per partnership + public static final String PA_REJECT_UNSIGNED_MESSAGES = "reject_unsigned_messages"; // Reject any messages that are sent to the partnership unisgned // A hopefully temporary key to maintain backwards compatibility public static final String USE_NEW_CERTIFICATE_LOOKUP_MODE = "use_new_certificate_lookup_mode"; @@ -186,6 +189,22 @@ public String getAlias(String partnershipType) throws OpenAS2Exception { return alias; } + public String getAliasFallback(String partnershipType) throws OpenAS2Exception { + String alias = null; + + if (partnershipType == PTYPE_RECEIVER) { + alias = getReceiverID(Partnership.PID_X509_ALIAS_FALLBACK); + } else if (partnershipType == PTYPE_SENDER) { + alias = getSenderID(Partnership.PID_X509_ALIAS_FALLBACK); + } + // The fallback is not guaranteed to be there so return null if not set + return alias; + } + + public boolean isRejectUnsignedMessages() throws OpenAS2Exception { + return getAttributeOrProperty(Partnership.PA_REJECT_UNSIGNED_MESSAGES, "false").equals("true"); + } + public String toString() { StringBuffer buf = new StringBuffer(); buf.append("Partnership " + getName()); diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 6418dbda..21c1fc75 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -149,10 +149,10 @@ private void loadAttributes(Node node, Partnership partnership) throws OpenAS2Ex } public void loadPartner(Map partners, Node node) throws OpenAS2Exception { - String[] requiredAttributes = {"name"}; + String[] requiredAttributes = {Partnership.PID_NAME}; Map newPartner = XMLUtil.mapAttributes(node, requiredAttributes); - String name = newPartner.get("name"); + String name = newPartner.get(Partnership.PID_NAME); if (partners.get(name) != null) { throw new OpenAS2Exception("Partner is defined more than once: " + name); @@ -172,7 +172,7 @@ private void loadPartnerIDs(Map partners, String partnershipName Map partnerAttr = XMLUtil.mapAttributes(partnerNode); // check for a partner name, and look up in partners list if one is found - String partnerName = partnerAttr.get("name"); + String partnerName = partnerAttr.get(Partnership.PID_NAME); if (partnerName != null) { @SuppressWarnings("unchecked") diff --git a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java index d5865f5d..0ae74c6d 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java +++ b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java @@ -329,13 +329,28 @@ protected String decryptAndVerify(AS2Message msg) throws OpenAS2Exception { if (LOG.isDebugEnabled()) { LOG.debug("decrypting :::" + msg.getLogMsgID()); } - - X509Certificate receiverCert = null; - PrivateKey receiverKey = null; String x509_alias = msg.getPartnership().getAlias(Partnership.PTYPE_RECEIVER); - receiverCert = certFx.getCertificate(x509_alias); - receiverKey = certFx.getPrivateKey(x509_alias); - msg.setData(AS2Util.getCryptoHelper().decrypt(msg.getData(), receiverCert, receiverKey)); + X509Certificate receiverCert = certFx.getCertificate(x509_alias); + PrivateKey receiverKey = certFx.getPrivateKey(x509_alias); + try { + msg.setData(AS2Util.getCryptoHelper().decrypt(msg.getData(), receiverCert, receiverKey)); + msg.setReceiverX509Alias(x509_alias); + } catch (Exception e) { + // Something went wrong - possibly a certificate change so try the backup if configured + String x509_alias_fallback = msg.getPartnership().getAliasFallback(Partnership.PTYPE_RECEIVER); + if (x509_alias_fallback == null) { + // No fallback so just throw the original exception + throw e; + } + receiverCert = certFx.getCertificate(x509_alias_fallback); + receiverKey = certFx.getPrivateKey(x509_alias_fallback); + msg.setData(AS2Util.getCryptoHelper().decrypt(msg.getData(), receiverCert, receiverKey)); + // success so the sender must have updated the receiver certificate + msg.setReceiverX509Alias(x509_alias_fallback); + // TODO: Automatically switch the alias in the partnerships.xml file and remove the fallback + // Send a message so that the certificate can be updated. + LOG.warn("Partner has updated our certificate. Switch the fallback alias and remove the X509 fallback for the partner: " + msg.getPartnership().getReceiverID(Partnership.PID_NAME)); + } if (LOG.isTraceEnabled() && "true".equalsIgnoreCase(System.getProperty("logRxdMsgMimeBodyParts", "false"))) { LOG.trace("Received MimeBodyPart for inbound message after decryption: " + msg.getLogMsgID() + "\n" + MimeUtil.toString(msg.getData(), true)); } @@ -371,7 +386,23 @@ protected String decryptAndVerify(AS2Message msg) throws OpenAS2Exception { } String x509_alias = msg.getPartnership().getAlias(Partnership.PTYPE_SENDER); X509Certificate senderCert = certFx.getCertificate(x509_alias); - msg.setData(AS2Util.getCryptoHelper().verifySignature(msg.getData(), senderCert)); + try { + msg.setData(AS2Util.getCryptoHelper().verifySignature(msg.getData(), senderCert)); + msg.setSenderX509Alias(x509_alias); + } catch (Exception e) { + // Something went wrong - possibly a certificate change so try the backup if configured + String x509_alias_fallback = msg.getPartnership().getAliasFallback(Partnership.PTYPE_SENDER); + if (x509_alias_fallback == null) { + // No fallback so just throw the original exception + throw e; + } + senderCert = certFx.getCertificate(x509_alias_fallback); + msg.setData(AS2Util.getCryptoHelper().verifySignature(msg.getData(), senderCert)); + msg.setSenderX509Alias(x509_alias_fallback); + // TODO: Automatically switch the alias in the partnerships.xml file and remove the fallback + // Send a message so that the certificate can be updated. + LOG.warn("Partner has updated their certificate. Switch the fallback alias and remove the X509 fallback for the partner: " + msg.getPartnership().getSenderID(Partnership.PID_NAME)); + } if (LOG.isTraceEnabled() && "true".equalsIgnoreCase(System.getProperty("logRxdMsgMimeBodyParts", "false"))) { LOG.trace("Received MimeBodyPart for inbound message after signature verification: " + msg.getLogMsgID() + "\n" + MimeUtil.toString(msg.getData(), true)); } @@ -381,7 +412,13 @@ protected String decryptAndVerify(AS2Message msg) throws OpenAS2Exception { LOG.error(msg, e); throw new DispositionException(new DispositionType("automatic-action", "MDN-sent-automatically", "processed", "Error", "integrity-check-failed"), AS2ReceiverModule.DISP_VERIFY_SIGNATURE_FAILED, e); } - + if (!msg.isRxdMsgWasSigned() && msg.getPartnership().isRejectUnsignedMessages()) { + // Configured to reject unsigned messages and this was not signed so... + throw new DispositionException( + new DispositionType("automatic-action", "MDN-sent-automatically", "processed", "Error", "signed-message-required"), + AS2ReceiverModule.DISP_ONLY_SIGNED_MESSAGES + ); + } if (LOG.isTraceEnabled()) { try { LOG.trace("SMIME Decrypted Content-Disposition: " + msg.getContentDisposition() + "\n Content-Type received: " + msg.getContentType() + "\n HEADERS after decryption: " + msg.getData().getAllHeaders() + "\n Content-Disposition in MSG getData() MIMEPART after decryption: " + msg.getData().getContentType()); diff --git a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverModule.java b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverModule.java index d3c4de1b..afb47af6 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverModule.java @@ -22,6 +22,7 @@ public class AS2ReceiverModule extends NetModule { public static final String DISP_DECRYPTION_ERROR = DP_HEADER + "but an error occured decrypting the content."; public static final String DISP_DECOMPRESSION_ERROR = DP_HEADER + "but an error occured decompressing the content."; public static final String DISP_VERIFY_SIGNATURE_FAILED = DP_DECRYPTED + "Authentication of the originator of the message failed."; + public static final String DISP_ONLY_SIGNED_MESSAGES = DP_HEADER + "Only signed messages are accepted."; public static final String DISP_CALC_MIC_FAILED = DP_DECRYPTED + "Calculation of the MIC for the message failed."; public static final String DISP_STORAGE_FAILED = DP_VERIFIED + " An error occured while storing the data to the file system."; public static final String DISP_SUCCESS = DP_VERIFIED + "There is no guarantee however that the EDI Interchange was syntactically correct, or was received by the EDI application/translator."; diff --git a/Server/src/main/java/org/openas2/util/AS2Util.java b/Server/src/main/java/org/openas2/util/AS2Util.java index 35236451..d6d1705c 100644 --- a/Server/src/main/java/org/openas2/util/AS2Util.java +++ b/Server/src/main/java/org/openas2/util/AS2Util.java @@ -641,7 +641,12 @@ public static void getMetaData(AS2Message msg, File inFile) throws OpenAS2Except public static void cleanupFiles(Message msg, boolean isError) { Log logger = LogFactory.getLog(AS2Util.class.getSimpleName()); - + if (msg.isFileCleanupCompleted()) { + if (logger.isTraceEnabled()) { + logger.trace("File cleanup already called for " + msg.getMessageID()); + } + return; + } String pendingInfoFileName = msg.getAttribute(FileAttribute.MA_PENDINGINFO); if (pendingInfoFileName != null) { File fPendingInfoFile = new File(pendingInfoFileName); @@ -727,6 +732,7 @@ public static void cleanupFiles(Message msg, boolean isError) { logger.error(msg, e); } } + msg.setFileCleanupCompleted(true); } private static String removeAngleBrackets(String srcString) { diff --git a/changes.txt b/changes.txt index 5b1a5f7a..1f2e6596 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,14 @@ +Version 3.3.0 - 2022-08-13 +This is a significant enhancement and minor bugfix release: + **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading + + 1. Support rejecting messages being sent that are unsigned. See section "Reject Unsigned Messages" in the documentation. + 2. Support having 2 certificates for your partner definition. This allows switch over of your own certificate to be zero down time. See section "Overlapping Old And New Certificates When Changing" in the documentation. + 3. Support overriding most of the config.xml attribute values using the external property file. This allows custom configuration to be restricted to a single properties file and makes version upgrades much simpler. + 4. Enhance shell and bat scripts to prepare for install and upgrade scripts to simplify managing the install and upgrade of OpenAS2 in the future. + 5. Enhance the properties parser to support periods in the property name. + 6. Fix the erroneous error when cleaning up files due to a duplicate call to the file cleanup function. + Version 3.2.1 - 2022-07-06 This is a bugfix release: **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading diff --git a/docs/OpenAS2HowTo.odt b/docs/OpenAS2HowTo.odt index 52dfbf03..8a7baaa8 100644 Binary files a/docs/OpenAS2HowTo.odt and b/docs/OpenAS2HowTo.odt differ diff --git a/docs/OpenAS2HowTo.pdf b/docs/OpenAS2HowTo.pdf index fd1d9cec..6922fd99 100644 Binary files a/docs/OpenAS2HowTo.pdf and b/docs/OpenAS2HowTo.pdf differ diff --git a/pom.xml b/pom.xml index f441509a..bd081ce5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.sf.openas2 OpenAS2 - 3.2.1 + 3.3.0 OpenAS2 pom @@ -51,28 +51,28 @@ org.bouncycastle - bcmail-jdk15on - 1.70 + bcmail-jdk18on + 1.71 org.bouncycastle - bcpkix-jdk15on - 1.70 + bcpkix-jdk18on + 1.71 org.bouncycastle - bcprov-jdk15on - 1.70 + bcprov-jdk18on + 1.71 org.bouncycastle - bcprov-ext-jdk15on - 1.70 + bcprov-ext-jdk18on + 1.71 org.bouncycastle - bcpg-jdk15on - 1.70 + bcpg-jdk18on + 1.71 org.apache.commons @@ -196,7 +196,7 @@ io.sentry sentry - 6.1.4 + 6.3.1