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