This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.18.x by this push:
     new 4ec9b37d70b0 CAMEL-24437/24439/24441/24443/24454: secure-default 
fixes, backport to camel-4.18.x
4ec9b37d70b0 is described below

commit 4ec9b37d70b08c478966000fb1bd653abfe410e3
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Aug 31 18:20:21 2026 +0200

    CAMEL-24437/24439/24441/24443/24454: secure-default fixes, backport to 
camel-4.18.x
    
    Backports five fixes already reviewed and merged on main (the same set 
backported
    to camel-4.22.x in #25944), grouped together because each changes a default 
or
    adds a check that was not there before. camel-mllp stops logging HL7 payload
    content unless logPhi=true is set explicitly. camel-knative with SSL 
enabled but
    no truststore configured no longer falls back to trusting every 
certificate; a
    deliberate trust.all property restores the old behaviour. camel-oauth now 
binds
    the authorization-code callback to the flow that started it via a random 
state
    parameter, checked with a constant-time comparison, closing a login-CSRF 
gap.
    camel-shiro verifies presented credentials on every exchange instead of 
accepting
    a subject already bound to the thread when alwaysReauthenticate=false.
    camel-crypto-pgp rejects decryption of a message with no 
integrity-protection
    packet unless requireIntegrityProtection=false is set.
    
    This branch has diverged from main in MllpSocketBuffer, the test layer, and 
the
    generated model files, so each commit needed manual adaptation rather than a
    mechanical cherry-pick: MllpSocketBuffer keeps this branch's synchronized 
methods
    rather than main's ReentrantLock form while carrying the substantive logging
    change across; the generated model/catalog/XSD/YAML-DSL files touched by
    CAMEL-24441 are regenerated from this branch rather than taken from main, 
since
    camel-java-io and the canonical/model YAML schemas don't exist here; and 
the new
    oauth and shiro tests use this branch's existing JUnit/camel-test-junit5 
style.
    Upgrade-guide entries are intentionally excluded — those live on main only.
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
    
    Closes #25946
---
 .../org/apache/camel/catalog/components/mllp.json  |  2 +-
 .../org/apache/camel/catalog/dataformats/pgp.json  |  3 +-
 .../org/apache/camel/catalog/models/pgp.json       |  3 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  | 12 +++
 .../apache/camel/catalog/schemas/camel-xml-io.xsd  | 12 +++
 .../converter/crypto/PGPDataFormatConfigurer.java  |  7 ++
 .../org/apache/camel/converter/crypto/pgp.json     |  3 +-
 .../converter/crypto/PGPKeyAccessDataFormat.java   | 29 +++++++
 .../camel/converter/crypto/PGPDataFormatTest.java  |  3 +
 .../crypto/PGPRequireIntegrityProtectionTest.java  | 97 ++++++++++++++++++++++
 .../knative/http/KnativeSslClientOptions.java      |  8 +-
 .../component/knative/http/KnativeHttpTest.java    |  3 +
 .../http/KnativeSslClientOptionsTrustTest.java     | 66 +++++++++++++++
 .../org/apache/camel/component/mllp/mllp.json      |  2 +-
 .../apache/camel/component/mllp/MllpComponent.java |  4 +-
 .../component/mllp/MllpTcpServerConsumer.java      |  4 +-
 .../camel/component/mllp/internal/Hl7Util.java     | 30 +++++++
 .../component/mllp/internal/MllpSocketBuffer.java  | 16 +++-
 .../mllp/internal/TcpSocketConsumerRunnable.java   | 10 +--
 .../apache/camel/component/mllp/LogPhiTest.java    |  6 +-
 .../camel/component/mllp/internal/Hl7UtilTest.java |  9 ++
 .../apache/camel/oauth/OAuthCodeFlowCallback.java  | 21 +++++
 .../apache/camel/oauth/OAuthCodeFlowProcessor.java | 19 ++++-
 .../java/org/apache/camel/oauth/OAuthSession.java  |  6 ++
 .../apache/camel/oauth/jakarta/ServletOAuth.java   |  3 +
 .../org/apache/camel/oauth/vertx/VertxOAuth.java   |  8 +-
 .../camel/oauth/OAuthProcessorFailClosedTest.java  | 85 +++++++++++++++++++
 .../shiro/security/ShiroSecurityProcessor.java     | 29 +++----
 ...oAuthenticationCredentialAlwaysCheckedTest.java | 93 +++++++++++++++++++++
 .../org/apache/camel/model/dataformat/pgp.json     |  3 +-
 .../camel/model/dataformat/PGPDataFormat.java      | 38 +++++++++
 .../reifier/dataformat/PGPDataFormatReifier.java   |  1 +
 .../java/org/apache/camel/xml/in/ModelParser.java  |  1 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  1 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  1 +
 .../component/dsl/MllpComponentBuilderFactory.java |  2 +-
 .../dsl/yaml/deserializers/ModelDeserializers.java |  6 ++
 .../generated/resources/schema/camelYamlDsl.json   |  6 ++
 38 files changed, 613 insertions(+), 39 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mllp.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mllp.json
index d52dd544be9c..637ac1d3bb85 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mllp.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mllp.json
@@ -47,7 +47,7 @@
     "tcpNoDelay": { "index": 20, "kind": "property", "displayName": "Tcp No 
Delay", "group": "producer", "label": "advanced,producer", "required": false, 
"type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.mllp.MllpConfiguration", 
"configurationField": "configuration", "description": "Enable\/disable the 
TCP_NODELAY socket option." },
     "autowiredEnabled": { "index": 21, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
     "defaultCharset": { "index": 22, "kind": "property", "displayName": 
"Default Charset", "group": "advanced", "label": "advanced", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "ISO-8859-1", 
"description": "Set the default character set to use for byte to\/from String 
conversions." },
-    "logPhi": { "index": 23, "kind": "property", "displayName": "Log Phi", 
"group": "advanced", "label": "advanced", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "Whether to log PHI" },
+    "logPhi": { "index": 23, "kind": "property", "displayName": "Log Phi", 
"group": "advanced", "label": "advanced", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "Whether to log PHI" },
     "logPhiMaxBytes": { "index": 24, "kind": "property", "displayName": "Log 
Phi Max Bytes", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": 5120, "description": "Set 
the maximum number of bytes of PHI that will be logged in a log entry." },
     "maxBufferSize": { "index": 25, "kind": "property", "displayName": "Max 
Buffer Size", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 1073741824, "configurationClass": 
"org.apache.camel.component.mllp.MllpConfiguration", "configurationField": 
"configuration", "description": "Maximum buffer size used when receiving or 
sending data over the wire." },
     "minBufferSize": { "index": 26, "kind": "property", "displayName": "Min 
Buffer Size", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 2048, "configurationClass": 
"org.apache.camel.component.mllp.MllpConfiguration", "configurationField": 
"configuration", "description": "Minimum buffer size used when receiving or 
sending data over the wire." },
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pgp.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pgp.json
index a32dcc611ca4..a9ac2d8c1649 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pgp.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pgp.json
@@ -30,6 +30,7 @@
     "algorithm": { "index": 11, "kind": "attribute", "displayName": 
"Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Symmetric key encryption algorithm; possible 
values are defined in org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; for 
example 2 (= TRIPLE DES), 3 (= CAST5), 4 (= BLOWFISH), 6 (= DES), 7 (= 
AES_128). Only relevant for encrypting." },
     "compressionAlgorithm": { "index": 12, "kind": "attribute", "displayName": 
"Compression Algorithm", "group": "common", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": 
false, "secret": false, "description": "Compression algorithm; possible values 
are defined in org.bouncycastle.bcpg.CompressionAlgorithmTags; for example 0 (= 
UNCOMPRESSED), 1 (= ZIP), 2 (= ZLIB), 3 (= BZIP2). Only relevant for 
encrypting." },
     "hashAlgorithm": { "index": 13, "kind": "attribute", "displayName": "Hash 
Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Signature hash algorithm; possible values are 
defined in org.bouncycastle.bcpg.HashAlgorithmTags; for example 2 (= SHA1), 8 
(= SHA256), 9 (= SHA384), 10 (= SHA512), 11 (=SHA224). Only relevant for 
signing." },
-    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "requireIntegrityProtection": { "index": 15, "kind": "attribute", 
"displayName": "Require Integrity Protection", "group": "common", "required": 
false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet carries no modification detection 
code, and the packet type is chosen  [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pgp.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pgp.json
index fda39c0a7b56..2548f5d96bb3 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pgp.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pgp.json
@@ -27,6 +27,7 @@
     "algorithm": { "index": 11, "kind": "attribute", "displayName": 
"Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Symmetric key encryption algorithm; possible 
values are defined in org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; for 
example 2 (= TRIPLE DES), 3 (= CAST5), 4 (= BLOWFISH), 6 (= DES), 7 (= 
AES_128). Only relevant for encrypting." },
     "compressionAlgorithm": { "index": 12, "kind": "attribute", "displayName": 
"Compression Algorithm", "group": "common", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": 
false, "secret": false, "description": "Compression algorithm; possible values 
are defined in org.bouncycastle.bcpg.CompressionAlgorithmTags; for example 0 (= 
UNCOMPRESSED), 1 (= ZIP), 2 (= ZLIB), 3 (= BZIP2). Only relevant for 
encrypting." },
     "hashAlgorithm": { "index": 13, "kind": "attribute", "displayName": "Hash 
Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Signature hash algorithm; possible values are 
defined in org.bouncycastle.bcpg.HashAlgorithmTags; for example 2 (= SHA1), 8 
(= SHA256), 9 (= SHA384), 10 (= SHA512), 11 (=SHA224). Only relevant for 
signing." },
-    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "requireIntegrityProtection": { "index": 15, "kind": "attribute", 
"displayName": "Require Integrity Protection", "group": "common", "required": 
false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet carries no modification detection 
code, and the packet type is chosen  [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 738e500259cd..1fc2f2e27cd6 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -10098,6 +10098,18 @@ required: The PGP message must contain at least one 
signature; if this is not th
 thrown. A signature verification is executed. ignore: Contained signatures in 
the PGP message are ignored; no signature
 verification is executed. no_signature_allowed: The PGP message must not 
contain a signature; otherwise an exception
 (PGPException) is thrown.
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="requireIntegrityProtection" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet
+carries no modification detection code, and the packet type is chosen by 
whoever produced the message, so accepting it
+lets the sender decide whether the integrity check applies. Set to false only 
to interoperate with a sender that still
+emits the legacy packet. Default value: true
 ]]>
             </xs:documentation>
           </xs:annotation>
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
index 4676541e2b31..38a70f0c7443 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
@@ -8771,6 +8771,18 @@ required: The PGP message must contain at least one 
signature; if this is not th
 thrown. A signature verification is executed. ignore: Contained signatures in 
the PGP message are ignored; no signature
 verification is executed. no_signature_allowed: The PGP message must not 
contain a signature; otherwise an exception
 (PGPException) is thrown.
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="requireIntegrityProtection" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet
+carries no modification detection code, and the packet type is chosen by 
whoever produced the message, so accepting it
+lets the sender decide whether the integrity check applies. Set to false only 
to interoperate with a sender that still
+emits the legacy packet. Default value: true
 ]]>
             </xs:documentation>
           </xs:annotation>
diff --git 
a/components/camel-crypto-pgp/src/generated/java/org/apache/camel/converter/crypto/PGPDataFormatConfigurer.java
 
b/components/camel-crypto-pgp/src/generated/java/org/apache/camel/converter/crypto/PGPDataFormatConfigurer.java
index 97f991c429d4..1e0bb26c42aa 100644
--- 
a/components/camel-crypto-pgp/src/generated/java/org/apache/camel/converter/crypto/PGPDataFormatConfigurer.java
+++ 
b/components/camel-crypto-pgp/src/generated/java/org/apache/camel/converter/crypto/PGPDataFormatConfigurer.java
@@ -31,6 +31,7 @@ public class PGPDataFormatConfigurer extends 
org.apache.camel.support.component.
         map.put("KeyUserid", java.lang.String.class);
         map.put("Password", java.lang.String.class);
         map.put("Provider", java.lang.String.class);
+        map.put("RequireIntegrityProtection", boolean.class);
         map.put("SignatureKeyFileName", java.lang.String.class);
         map.put("SignatureKeyRing", byte[].class);
         map.put("SignatureKeyUserid", java.lang.String.class);
@@ -56,6 +57,8 @@ public class PGPDataFormatConfigurer extends 
org.apache.camel.support.component.
         case "keyUserid": target.setKeyUserid(property(camelContext, 
java.lang.String.class, value)); return true;
         case "password": target.setPassword(property(camelContext, 
java.lang.String.class, value)); return true;
         case "provider": target.setProvider(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "requireintegrityprotection":
+        case "requireIntegrityProtection": 
target.setRequireIntegrityProtection(property(camelContext, boolean.class, 
value)); return true;
         case "signaturekeyfilename":
         case "signatureKeyFileName": 
target.setSignatureKeyFileName(property(camelContext, java.lang.String.class, 
value)); return true;
         case "signaturekeyring":
@@ -91,6 +94,8 @@ public class PGPDataFormatConfigurer extends 
org.apache.camel.support.component.
         case "keyUserid": return java.lang.String.class;
         case "password": return java.lang.String.class;
         case "provider": return java.lang.String.class;
+        case "requireintegrityprotection":
+        case "requireIntegrityProtection": return boolean.class;
         case "signaturekeyfilename":
         case "signatureKeyFileName": return java.lang.String.class;
         case "signaturekeyring":
@@ -122,6 +127,8 @@ public class PGPDataFormatConfigurer extends 
org.apache.camel.support.component.
         case "keyUserid": return target.getKeyUserid();
         case "password": return target.getPassword();
         case "provider": return target.getProvider();
+        case "requireintegrityprotection":
+        case "requireIntegrityProtection": return 
target.isRequireIntegrityProtection();
         case "signaturekeyfilename":
         case "signatureKeyFileName": return target.getSignatureKeyFileName();
         case "signaturekeyring":
diff --git 
a/components/camel-crypto-pgp/src/generated/resources/META-INF/org/apache/camel/converter/crypto/pgp.json
 
b/components/camel-crypto-pgp/src/generated/resources/META-INF/org/apache/camel/converter/crypto/pgp.json
index a32dcc611ca4..a9ac2d8c1649 100644
--- 
a/components/camel-crypto-pgp/src/generated/resources/META-INF/org/apache/camel/converter/crypto/pgp.json
+++ 
b/components/camel-crypto-pgp/src/generated/resources/META-INF/org/apache/camel/converter/crypto/pgp.json
@@ -30,6 +30,7 @@
     "algorithm": { "index": 11, "kind": "attribute", "displayName": 
"Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Symmetric key encryption algorithm; possible 
values are defined in org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; for 
example 2 (= TRIPLE DES), 3 (= CAST5), 4 (= BLOWFISH), 6 (= DES), 7 (= 
AES_128). Only relevant for encrypting." },
     "compressionAlgorithm": { "index": 12, "kind": "attribute", "displayName": 
"Compression Algorithm", "group": "common", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": 
false, "secret": false, "description": "Compression algorithm; possible values 
are defined in org.bouncycastle.bcpg.CompressionAlgorithmTags; for example 0 (= 
UNCOMPRESSED), 1 (= ZIP), 2 (= ZLIB), 3 (= BZIP2). Only relevant for 
encrypting." },
     "hashAlgorithm": { "index": 13, "kind": "attribute", "displayName": "Hash 
Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Signature hash algorithm; possible values are 
defined in org.bouncycastle.bcpg.HashAlgorithmTags; for example 2 (= SHA1), 8 
(= SHA256), 9 (= SHA384), 10 (= SHA512), 11 (=SHA224). Only relevant for 
signing." },
-    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "requireIntegrityProtection": { "index": 15, "kind": "attribute", 
"displayName": "Require Integrity Protection", "group": "common", "required": 
false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet carries no modification detection 
code, and the packet type is chosen  [...]
   }
 }
diff --git 
a/components/camel-crypto-pgp/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
 
b/components/camel-crypto-pgp/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
index 36fca4cee3b2..3f51111a400c 100644
--- 
a/components/camel-crypto-pgp/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
+++ 
b/components/camel-crypto-pgp/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
@@ -163,6 +163,8 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
 
     private String signatureVerificationOption = "optional";
 
+    private boolean requireIntegrityProtection = true;
+
     /*
      * The default value "_CONSOLE" marks the file as For Your Eyes Only... may
      * cause problems for the receiver if they use an automated process to
@@ -418,6 +420,14 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
                 if (!pbe.verify()) {
                     throw new PGPException("Message failed integrity check");
                 }
+            } else if (requireIntegrityProtection) {
+                // Without a modification detection code the ciphertext is 
malleable, and whether the check runs
+                // would otherwise be decided by the packet type the sender 
chose - so a sender or an intermediary
+                // could drop the protection simply by sending the legacy 
packet.
+                throw new PGPException(
+                        "PGP message is not integrity protected. Either send 
an integrity protected message, or set"
+                                       + " requireIntegrityProtection=false on 
the PGP decryptor to accept the legacy"
+                                       + " symmetrically encrypted data 
packet.");
             }
         } finally {
             IOHelper.close(osb, litData, uncompressedData, encData, in, 
encryptedStream);
@@ -690,6 +700,25 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
         this.secretKeyAccessor = secretKeyAccessor;
     }
 
+    public boolean isRequireIntegrityProtection() {
+        return requireIntegrityProtection;
+    }
+
+    /**
+     * Whether a message must be integrity protected in order to be decrypted.
+     * <p>
+     * An OpenPGP symmetrically encrypted integrity protected data packet 
carries a modification detection code, which
+     * {@code unmarshal} verifies. The older symmetrically encrypted data 
packet carries none, and OpenPGP's CFB mode is
+     * malleable without it, so accepting such a message means accepting 
ciphertext that may have been altered. Since
+     * the packet type is chosen by whoever produced the message, leaving this 
off lets the sender - or anyone who can
+     * rewrite the message in transit - decide whether the check applies at 
all.
+     * <p>
+     * Defaults to true. Set it to false only to interoperate with a sender 
that still emits the legacy packet.
+     */
+    public void setRequireIntegrityProtection(boolean 
requireIntegrityProtection) {
+        this.requireIntegrityProtection = requireIntegrityProtection;
+    }
+
     public String getSignatureVerificationOption() {
         return signatureVerificationOption;
     }
diff --git 
a/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
 
b/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
index 0f03b442477d..73ea555275b0 100644
--- 
a/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
+++ 
b/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
@@ -98,6 +98,9 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         decryptor.setSignatureKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
         decryptor.setPassword("Abcd1234");
         decryptor.setSignatureKeyUserid("keyflag");
+        // the encryptor above deliberately emits the legacy packet, which 
carries no modification detection code,
+        // so the decryptor has to accept messages that are not integrity 
protected
+        decryptor.setRequireIntegrityProtection(false);
     }
 
     protected String getKeyFileName() {
diff --git 
a/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPRequireIntegrityProtectionTest.java
 
b/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPRequireIntegrityProtectionTest.java
new file mode 100644
index 000000000000..fa1f5053f7dd
--- /dev/null
+++ 
b/components/camel-crypto-pgp/src/test/java/org/apache/camel/converter/crypto/PGPRequireIntegrityProtectionTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The legacy symmetrically encrypted data packet carries no modification 
detection code, and OpenPGP's CFB mode is
+ * malleable without one. Because the packet type is chosen by whoever 
produced the message, a decryptor that only
+ * checks integrity when the message claims to be protected lets the sender 
decide whether the check runs at all.
+ */
+class PGPRequireIntegrityProtectionTest {
+
+    private static final String PUB_KEY_RING = 
"org/apache/camel/component/crypto/pubring.gpg";
+    private static final String SEC_KEY_RING = 
"org/apache/camel/component/crypto/secring.gpg";
+    private static final String PAYLOAD = "Hello PGP";
+
+    @BeforeAll
+    static void installProvider() {
+        java.security.Security.addProvider(new BouncyCastleProvider());
+    }
+
+    @Test
+    void aMessageWithoutIntegrityProtectionIsRejectedByDefault() throws 
Exception {
+        PGPDataFormat decryptor = decryptor();
+        assertTrue(decryptor.isRequireIntegrityProtection(), "expected the 
secure default");
+
+        Exception e = assertThrows(PGPException.class, () -> 
roundTrip(decryptor));
+        assertTrue(e.getMessage().contains("not integrity protected"), 
"unexpected message: " + e.getMessage());
+    }
+
+    @Test
+    void theLegacyPacketCanStillBeAcceptedOnPurpose() throws Exception {
+        PGPDataFormat decryptor = decryptor();
+        decryptor.setRequireIntegrityProtection(false);
+
+        assertEquals(PAYLOAD, roundTrip(decryptor));
+    }
+
+    /**
+     * Encrypts with {@code integrity=false}, so the message carries no 
modification detection code, then decrypts it
+     * with the given decryptor.
+     */
+    private static String roundTrip(PGPDataFormat decryptor) throws Exception {
+        PGPDataFormat encryptor = new PGPDataFormat();
+        encryptor.setKeyFileName(PUB_KEY_RING);
+        encryptor.setKeyUserid("sdude");
+        encryptor.setIntegrity(false);
+
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            context.start();
+            encryptor.start();
+            decryptor.start();
+
+            Exchange exchange = new DefaultExchange(context);
+            ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
+            encryptor.marshal(exchange, 
PAYLOAD.getBytes(StandardCharsets.UTF_8), encrypted);
+
+            Object body = decryptor.unmarshal(exchange, 
encrypted.toByteArray());
+            return context.getTypeConverter().convertTo(String.class, 
exchange, body);
+        }
+    }
+
+    private static PGPDataFormat decryptor() {
+        PGPDataFormat decryptor = new PGPDataFormat();
+        decryptor.setKeyFileName(SEC_KEY_RING);
+        decryptor.setPassword("sdude");
+        return decryptor;
+    }
+}
diff --git 
a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeSslClientOptions.java
 
b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeSslClientOptions.java
index 65ec42fa3da9..67ac05a7ce4e 100644
--- 
a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeSslClientOptions.java
+++ 
b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeSslClientOptions.java
@@ -116,10 +116,16 @@ public class KnativeSslClientOptions extends 
WebClientOptions implements CamelCo
             } else if (trustCertPath.isPresent()) {
                 String[] trustCertPathItems = trustCertPath.get().split(",");
                 setTrustCertPath(trustCertPathItems);
-            } else {
+            } else if (Boolean.parseBoolean(
+                    propertiesComponent.resolveProperty(PROPERTY_PREFIX + 
"trust.all").orElse("false"))) {
+                // Explicitly asked for. Useful against a development cluster 
with a self-signed
+                // certificate, but it has to be requested rather than being 
what "no truststore" means.
                 trustOptions = TrustAllOptions.INSTANCE;
                 setTrustOptions(trustOptions);
             }
+            // Otherwise leave the trust options unset, so the JVM default 
trust anchors apply - the same
+            // fallback SSLContextParameters and the rest of Camel use. 
Turning TLS on must not be the
+            // thing that turns certificate validation off.
         }
     }
 
diff --git 
a/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java
 
b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java
index 8a7d24be6c51..3b0002939429 100644
--- 
a/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java
+++ 
b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java
@@ -2249,6 +2249,9 @@ public class KnativeHttpTest {
         
context.getPropertiesComponent().addInitialProperty("camel.knative.client.ssl.verify.hostname",
 "false");
         
context.getPropertiesComponent().addInitialProperty("camel.knative.client.ssl.key.path",
 "keystore/client.pem");
         
context.getPropertiesComponent().addInitialProperty("camel.knative.client.ssl.key.cert.path",
 "keystore/client.crt");
+        // The test server presents a self-signed certificate. Enabling SSL no 
longer implies trusting every
+        // certificate, so the trust decision has to be made here.
+        
context.getPropertiesComponent().addInitialProperty("camel.knative.client.ssl.trust.all",
 "true");
 
         KnativeComponent component = configureKnativeComponent(
                 context,
diff --git 
a/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeSslClientOptionsTrustTest.java
 
b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeSslClientOptionsTrustTest.java
new file mode 100644
index 000000000000..2abecc3a4da2
--- /dev/null
+++ 
b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeSslClientOptionsTrustTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.knative.http;
+
+import java.util.Properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Enabling SSL is not a request to stop validating certificates. With no 
truststore and no trust certificates
+ * configured the client has to fall back to the JVM default trust anchors, 
the way the rest of Camel does; accepting
+ * every certificate is available, but only when it is asked for.
+ */
+class KnativeSslClientOptionsTrustTest {
+
+    @Test
+    void sslEnabledWithoutATruststoreDoesNotTrustEveryCertificate() throws 
Exception {
+        try (CamelContext context = 
contextWith("camel.knative.client.ssl.enabled", "true")) {
+            KnativeSslClientOptions options = new 
KnativeSslClientOptions(context);
+
+            assertThat(options.isSslEnabled()).isTrue();
+            // Left unset, so Vert.x falls back to the JVM default trust 
anchors
+            assertThat(options.getTrustOptions()).isNull();
+        }
+    }
+
+    @Test
+    void trustAllIsAvailableButHasToBeRequested() throws Exception {
+        try (CamelContext context = contextWith(
+                "camel.knative.client.ssl.enabled", "true",
+                "camel.knative.client.ssl.trust.all", "true")) {
+            KnativeSslClientOptions options = new 
KnativeSslClientOptions(context);
+
+            
assertThat(options.getTrustOptions()).isInstanceOf(TrustAllOptions.class);
+        }
+    }
+
+    private static CamelContext contextWith(String... keyValues) throws 
Exception {
+        Properties properties = new Properties();
+        for (int i = 0; i < keyValues.length; i += 2) {
+            properties.setProperty(keyValues[i], keyValues[i + 1]);
+        }
+        CamelContext context = new DefaultCamelContext();
+        context.getPropertiesComponent().setInitialProperties(properties);
+        context.start();
+        return context;
+    }
+}
diff --git 
a/components/camel-mllp/src/generated/resources/META-INF/org/apache/camel/component/mllp/mllp.json
 
b/components/camel-mllp/src/generated/resources/META-INF/org/apache/camel/component/mllp/mllp.json
index d52dd544be9c..637ac1d3bb85 100644
--- 
a/components/camel-mllp/src/generated/resources/META-INF/org/apache/camel/component/mllp/mllp.json
+++ 
b/components/camel-mllp/src/generated/resources/META-INF/org/apache/camel/component/mllp/mllp.json
@@ -47,7 +47,7 @@
     "tcpNoDelay": { "index": 20, "kind": "property", "displayName": "Tcp No 
Delay", "group": "producer", "label": "advanced,producer", "required": false, 
"type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.mllp.MllpConfiguration", 
"configurationField": "configuration", "description": "Enable\/disable the 
TCP_NODELAY socket option." },
     "autowiredEnabled": { "index": 21, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
     "defaultCharset": { "index": 22, "kind": "property", "displayName": 
"Default Charset", "group": "advanced", "label": "advanced", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "ISO-8859-1", 
"description": "Set the default character set to use for byte to\/from String 
conversions." },
-    "logPhi": { "index": 23, "kind": "property", "displayName": "Log Phi", 
"group": "advanced", "label": "advanced", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "Whether to log PHI" },
+    "logPhi": { "index": 23, "kind": "property", "displayName": "Log Phi", 
"group": "advanced", "label": "advanced", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "Whether to log PHI" },
     "logPhiMaxBytes": { "index": 24, "kind": "property", "displayName": "Log 
Phi Max Bytes", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": 5120, "description": "Set 
the maximum number of bytes of PHI that will be logged in a log entry." },
     "maxBufferSize": { "index": 25, "kind": "property", "displayName": "Max 
Buffer Size", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 1073741824, "configurationClass": 
"org.apache.camel.component.mllp.MllpConfiguration", "configurationField": 
"configuration", "description": "Maximum buffer size used when receiving or 
sending data over the wire." },
     "minBufferSize": { "index": 26, "kind": "property", "displayName": "Min 
Buffer Size", "group": "advanced", "label": "advanced", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 2048, "configurationClass": 
"org.apache.camel.component.mllp.MllpConfiguration", "configurationField": 
"configuration", "description": "Minimum buffer size used when receiving or 
sending data over the wire." },
diff --git 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
index bffd141c42e4..e0b6eef63d5d 100644
--- 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
+++ 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
@@ -29,8 +29,8 @@ import org.apache.camel.support.DefaultComponent;
 @Component("mllp")
 public class MllpComponent extends DefaultComponent implements 
SSLContextParametersAware {
 
-    @Metadata(label = "advanced", defaultValue = "true")
-    private boolean logPhi = true;
+    @Metadata(label = "advanced", defaultValue = "false")
+    private boolean logPhi;
     @Metadata(label = "advanced", defaultValue = "5120")
     private int logPhiMaxBytes = 5120;
     @Metadata(label = "advanced", defaultValue = "ISO-8859-1")
diff --git 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java
 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java
index 638d8537f6d4..4d88b7565959 100644
--- 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java
+++ 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java
@@ -633,7 +633,7 @@ public class MllpTcpServerConsumer extends DefaultConsumer {
                 log.debug("sendAcknowledgement(originalHl7MessageBytes[{}], 
Exchange[{}], {}) - Sending Acknowledgement: {}",
                         originalHl7MessageBytes == null ? -1 : 
originalHl7MessageBytes.length, exchange.getExchangeId(),
                         consumerRunnable.getSocket(),
-                        
consumerRunnable.getMllpBuffer().toPrintFriendlyHl7String());
+                        
hl7Util.convertToLoggableString(consumerRunnable.getMllpBuffer().toMllpPayload()));
             }
 
             try {
@@ -657,7 +657,7 @@ public class MllpTcpServerConsumer extends DefaultConsumer {
                 log.debug("sendAcknowledgement(originalHl7MessageBytes[{}], 
Exchange[{}], {}) - Sending Acknowledgement: {}",
                         originalHl7MessageBytes == null ? -1 : 
originalHl7MessageBytes.length, exchange.getExchangeId(),
                         consumerRunnable.getSocket(),
-                        
hl7Util.convertToPrintFriendlyString(acknowledgementMessageBytes));
+                        
hl7Util.convertToLoggableString(acknowledgementMessageBytes));
             }
 
             try {
diff --git 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
index 4d2c39e8eb02..9a69beab833d 100644
--- 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
+++ 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
@@ -32,6 +32,11 @@ import org.slf4j.LoggerFactory;
 public final class Hl7Util {
     public static final String NULL_REPLACEMENT_VALUE = "<null>";
     public static final String EMPTY_REPLACEMENT_VALUE = "<>";
+    /**
+     * Substituted for message content when {@code logPhi} is disabled. The 
conversion methods are reached from log
+     * statements and from exception messages, so honouring the flag here 
covers both.
+     */
+    public static final String PHI_SUPPRESSED_REPLACEMENT_VALUE = "<PHI 
suppressed>";
 
     public static final Map<Character, String> CHARACTER_REPLACEMENTS;
 
@@ -91,6 +96,31 @@ public final class Hl7Util {
         return logPhiMaxBytes;
     }
 
+    public boolean isLogPhi() {
+        return logPhi;
+    }
+
+    /**
+     * Message content for a log statement: the content itself when {@code 
logPhi} is enabled, a placeholder otherwise.
+     * <p>
+     * Deliberately separate from {@link 
#convertToPrintFriendlyString(byte[])}, which is not a logging helper - it also
+     * extracts the MSH-9 field when an acknowledgement is generated, so 
redacting inside it corrupts the
+     * acknowledgement rather than the log.
+     */
+    public String convertToLoggableString(byte[] phiBytes) {
+        if (!logPhi) {
+            return PHI_SUPPRESSED_REPLACEMENT_VALUE;
+        }
+        return convertToPrintFriendlyString(phiBytes);
+    }
+
+    public String convertToLoggableString(byte[] phiBytes, int startPosition, 
int endPosition) {
+        if (!logPhi) {
+            return PHI_SUPPRESSED_REPLACEMENT_VALUE;
+        }
+        return convertToPrintFriendlyString(phiBytes, startPosition, 
endPosition);
+    }
+
     public String generateInvalidPayloadExceptionMessage(final byte[] 
hl7Bytes) {
         if (hl7Bytes == null) {
             return "HL7 payload is null";
diff --git 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
index 73ed4ea0b62c..047f0d0c57b8 100644
--- 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
+++ 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
@@ -209,7 +209,7 @@ public class MllpSocketBuffer {
                 if (size() > 0 && !hasCompleteEnvelope()) {
                     if (!hasEndOfData() && hasEndOfBlock() && endOfBlockIndex 
< size() - 1) {
                         LOG.warn("readFrom({}, {}, {}) - exiting with partial 
payload {}", socket, receiveTimeout, readTimeout,
-                                hl7Util.convertToPrintFriendlyString(buffer, 
0, size() - 1));
+                                hl7Util.convertToLoggableString(buffer, 0, 
size() - 1));
                     }
                 }
             }
@@ -327,6 +327,18 @@ public class MllpSocketBuffer {
         return answer;
     }
 
+    /**
+     * The buffer content for a log statement, honouring the component's 
{@code logPhi} setting, and resetting the
+     * buffer either way so the caller's behaviour does not depend on whether 
logging is enabled.
+     */
+    public synchronized String toLoggableStringAndReset() {
+        String answer = hl7Util.isLogPhi() ? toPrintFriendlyString() : 
Hl7Util.PHI_SUPPRESSED_REPLACEMENT_VALUE;
+
+        reset();
+
+        return answer;
+    }
+
     public synchronized String toHl7String() {
         return this.toHl7String(charset);
     }
@@ -562,7 +574,7 @@ public class MllpSocketBuffer {
                 } else {
                     LOG.warn(
                             "readSocketInputStream(socketInputStream, {}) - 
ignoring {} bytes received before START_OF_BLOCK: {}",
-                            socket, size(), toPrintFriendlyStringAndReset());
+                            socket, size(), toLoggableStringAndReset());
                 }
             }
         } catch (SocketTimeoutException timeoutEx) {
diff --git 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
index f8c6da04cd7a..a35af9e909bb 100644
--- 
a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
+++ 
b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
@@ -149,16 +149,16 @@ public class TcpSocketConsumerRunnable implements 
Runnable {
                         hl7MessageBytes = mllpBuffer.toMllpPayload();
                         if (log.isDebugEnabled()) {
                             log.debug("Received {} byte message {}", 
hl7MessageBytes.length,
-                                    
hl7Util.convertToPrintFriendlyString(hl7MessageBytes));
+                                    
hl7Util.convertToLoggableString(hl7MessageBytes));
                         }
                         if (mllpBuffer.hasLeadingOutOfBandData()) {
                             // TODO:  Move the conversion utilities to the 
MllpSocketBuffer to avoid a byte[] copy
                             log.warn("Ignoring leading out-of-band data: {}",
-                                    
hl7Util.convertToPrintFriendlyString(mllpBuffer.getLeadingOutOfBandData()));
+                                    
hl7Util.convertToLoggableString(mllpBuffer.getLeadingOutOfBandData()));
                         }
                         if (mllpBuffer.hasTrailingOutOfBandData()) {
                             log.warn("Ignoring trailing out-of-band data: {}",
-                                    
hl7Util.convertToPrintFriendlyString(mllpBuffer.getTrailingOutOfBandData()));
+                                    
hl7Util.convertToLoggableString(mllpBuffer.getTrailingOutOfBandData()));
                         }
                         mllpBuffer.reset();
 
@@ -166,12 +166,12 @@ public class TcpSocketConsumerRunnable implements 
Runnable {
                     } else if (!mllpBuffer.hasStartOfBlock()) {
                         byte[] payload = mllpBuffer.toByteArray();
                         log.warn("Ignoring {} byte un-enveloped payload {}", 
payload.length,
-                                hl7Util.convertToPrintFriendlyString(payload));
+                                hl7Util.convertToLoggableString(payload));
                         mllpBuffer.reset();
                     } else if (!mllpBuffer.isEmpty()) {
                         byte[] payload = mllpBuffer.toByteArray();
                         log.warn("Partial {} byte payload received {}", 
payload.length,
-                                hl7Util.convertToPrintFriendlyString(payload));
+                                hl7Util.convertToLoggableString(payload));
                     }
                 } catch (SocketTimeoutException timeoutEx) {
                     if (mllpBuffer.isEmpty()) {
diff --git 
a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/LogPhiTest.java
 
b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/LogPhiTest.java
index dc80cb5bd1da..39eb4705aab3 100644
--- 
a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/LogPhiTest.java
+++ 
b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/LogPhiTest.java
@@ -107,9 +107,13 @@ public class LogPhiTest extends CamelTestSupport {
         testLogPhi(startLogPhi, exceptionMessage -> 
assertTrue(exceptionMessage.contains("hl7Message")));
     }
 
+    /**
+     * logPhi now defaults to false, so the default behaves like 
testLogPhiFalse: for a protocol whose payload is
+     * patient data by definition, content stays out of the log unless it is 
asked for.
+     */
     @Test
     public void testLogPhiDefault() throws Exception {
-        testLogPhi(startDefaultPhi, exceptionMessage -> 
assertTrue(exceptionMessage.contains("hl7Message")));
+        testLogPhi(startDefaultPhi, exceptionMessage -> 
assertFalse(exceptionMessage.contains("hl7Message")));
     }
 
     public void testLogPhi(Endpoint endpoint, Consumer<String> contains) {
diff --git 
a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
 
b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
index 0103c2a4bfbc..82b1cba72ba8 100644
--- 
a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
+++ 
b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
@@ -682,6 +682,15 @@ public class Hl7UtilTest {
         assertEquals("MSH", result);
     }
 
+    @Test
+    public void testConvertToLoggableStringHonorsLogPhi() {
+        Hl7Util suppressed = new Hl7Util(5120, false);
+
+        assertEquals(Hl7Util.PHI_SUPPRESSED_REPLACEMENT_VALUE,
+                suppressed.convertToLoggableString(TEST_MESSAGE_BYTES));
+        assertEquals(EXPECTED_MESSAGE, 
hl7util.convertToLoggableString(TEST_MESSAGE_BYTES));
+    }
+
     @Test
     public void testGenerateAcknowledgementPayloadWithOnlyMsh91() throws 
Exception {
         final MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new 
MllpEndpointStub());
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
index 677305dd5cf2..c80eda2a7763 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
@@ -16,12 +16,16 @@
  */
 package org.apache.camel.oauth;
 
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
 import org.apache.camel.Exchange;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static org.apache.camel.oauth.OAuth.CAMEL_OAUTH_REDIRECT_URI;
 import static org.apache.camel.oauth.OAuthProperties.getRequiredProperty;
+import static org.apache.camel.oauth.OAuthSession.OAUTH_STATE;
 
 public class OAuthCodeFlowCallback extends AbstractOAuthProcessor {
 
@@ -48,6 +52,23 @@ public class OAuthCodeFlowCallback extends 
AbstractOAuthProcessor {
         var oauth = findOAuthOrThrow(context);
         var session = oauth.getOrCreateSession(exchange);
 
+        // The state must match the one this session sent to the identity 
provider, and it is single use.
+        // Without that binding any authorization code, obtained in any 
browser, could be redeemed here and
+        // bound to whichever session presented it - which is login CSRF.
+        var expectedState = session.<String> 
removeValue(OAUTH_STATE).orElse(null);
+        var actualState = msg.getHeader("state", String.class);
+        if (expectedState == null) {
+            log.error("No authorization code flow is in progress for this 
session");
+            reject(exchange, 400, "No authorization code flow in progress");
+            return;
+        }
+        if (actualState == null || !MessageDigest.isEqual(
+                expectedState.getBytes(StandardCharsets.UTF_8), 
actualState.getBytes(StandardCharsets.UTF_8))) {
+            log.error("Authorization state does not match the flow started by 
this session");
+            reject(exchange, 400, "Authorization state mismatch");
+            return;
+        }
+
         // Exchange the authorization code for access/refresh/id tokens
         //
         String redirectUri = getRequiredProperty(exchange.getContext(), 
CAMEL_OAUTH_REDIRECT_URI);
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
index 2de2d8969b28..0ef855c34c04 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.oauth;
 
+import java.security.SecureRandom;
+import java.util.Base64;
+
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.slf4j.Logger;
@@ -23,9 +26,12 @@ import org.slf4j.LoggerFactory;
 
 import static org.apache.camel.oauth.OAuth.CAMEL_OAUTH_REDIRECT_URI;
 import static org.apache.camel.oauth.OAuthProperties.getRequiredProperty;
+import static org.apache.camel.oauth.OAuthSession.OAUTH_STATE;
 
 public class OAuthCodeFlowProcessor extends AbstractOAuthProcessor {
 
+    private static final SecureRandom RANDOM = new SecureRandom();
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     @Override
@@ -65,8 +71,13 @@ public class OAuthCodeFlowProcessor extends 
AbstractOAuthProcessor {
         log.info("Register post login url: {}", postLoginUrl);
         session.putValue("OAuthPostLoginUrl", postLoginUrl);
 
+        // RFC 6749 section 10.12: bind the callback to a flow this session 
actually started, so an
+        // authorization code obtained elsewhere cannot be replayed into this 
session's callback.
+        var state = newState();
+        session.putValue(OAUTH_STATE, state);
+
         var redirectUri = getRequiredProperty(exchange.getContext(), 
CAMEL_OAUTH_REDIRECT_URI);
-        var params = new OAuthCodeFlowParams().setRedirectUri(redirectUri);
+        var params = new 
OAuthCodeFlowParams().setRedirectUri(redirectUri).setState(state);
         var authRequestUrl = oauth.buildCodeFlowAuthRequestUrl(params);
 
         sendRedirect(msg, authRequestUrl);
@@ -75,6 +86,12 @@ public class OAuthCodeFlowProcessor extends 
AbstractOAuthProcessor {
         exchange.setRouteStop(true);
     }
 
+    private static String newState() {
+        var bytes = new byte[32];
+        RANDOM.nextBytes(bytes);
+        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
+    }
+
     private String getPostLoginUrl(Message msg) {
         String postLoginUrl;
         var xProto = msg.getHeader("X-Forwarded-Proto", String.class);
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthSession.java 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthSession.java
index 30a986f5e504..30a7b6ef3816 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthSession.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthSession.java
@@ -20,6 +20,12 @@ import java.util.Optional;
 
 public interface OAuthSession {
 
+    /**
+     * Session key holding the {@code state} value of an in-flight 
authorization code flow. The callback accepts a code
+     * only when the request carries the same value, which is what ties the 
callback to a flow this session started.
+     */
+    String OAUTH_STATE = "OAuthState";
+
     String getSessionId();
 
     <T> Optional<T> getValue(String key, Class<T> clazz);
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/jakarta/ServletOAuth.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/jakarta/ServletOAuth.java
index 3c3b62ca1f1b..50cce2027bdb 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/jakarta/ServletOAuth.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/jakarta/ServletOAuth.java
@@ -105,6 +105,9 @@ public class ServletOAuth extends OAuth {
             uriBuilder.addParameter("response_type", 
params.getResponseType().toString().toLowerCase());
             uriBuilder.addParameter("client_id", params.getClientId());
             uriBuilder.addParameter("redirect_uri", params.getRedirectUri());
+            if (params.getState() != null) {
+                uriBuilder.addParameter("state", params.getState());
+            }
             var requestUrl = uriBuilder.build().toString();
             return requestUrl;
         } catch (URISyntaxException ex) {
diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/vertx/VertxOAuth.java
 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/vertx/VertxOAuth.java
index 09c27afda954..b7a98acc54a0 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/vertx/VertxOAuth.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/vertx/VertxOAuth.java
@@ -95,9 +95,13 @@ public class VertxOAuth extends OAuth {
         if (params.getScopes() == null) {
             params.setScope("openid");
         }
-        return oauth2.authorizeURL(new OAuth2AuthorizationURL()
+        var url = new OAuth2AuthorizationURL()
                 .setRedirectUri(params.getRedirectUri())
-                .setScopes(params.getScopes()));
+                .setScopes(params.getScopes());
+        if (params.getState() != null) {
+            url.setState(params.getState());
+        }
+        return oauth2.authorizeURL(url);
     }
 
     @Override
diff --git 
a/components/camel-oauth/src/test/java/org/apache/camel/oauth/OAuthProcessorFailClosedTest.java
 
b/components/camel-oauth/src/test/java/org/apache/camel/oauth/OAuthProcessorFailClosedTest.java
index 4da45757e97e..a619c95c4c50 100644
--- 
a/components/camel-oauth/src/test/java/org/apache/camel/oauth/OAuthProcessorFailClosedTest.java
+++ 
b/components/camel-oauth/src/test/java/org/apache/camel/oauth/OAuthProcessorFailClosedTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.oauth;
 
+import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.support.DefaultExchange;
@@ -68,4 +69,88 @@ class OAuthProcessorFailClosedTest {
             assertTrue(exchange.isRouteStop());
         }
     }
+
+    /**
+     * The state check runs before the authorization code is redeemed, so 
these paths are reachable without an identity
+     * provider. Without the check, an authorization code obtained in any 
browser could be redeemed at this callback and
+     * bound to whichever session presented it.
+     */
+    @Test
+    void aCallbackWithNoFlowInProgressStopsTheRoute() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Exchange exchange = new DefaultExchange(context);
+            exchange.getMessage().setHeader("code", "an-authorization-code");
+            exchange.getMessage().setHeader("state", 
"a-state-we-never-issued");
+
+            bindTestOAuth(context);
+            new OAuthCodeFlowCallback().process(exchange);
+
+            assertEquals(400, 
exchange.getMessage().getHeader(Exchange.HTTP_RESPONSE_CODE));
+            assertEquals("No authorization code flow in progress", 
exchange.getMessage().getBody());
+            assertTrue(exchange.isRouteStop());
+        }
+    }
+
+    @Test
+    void aCallbackWithTheWrongStateStopsTheRoute() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Exchange exchange = new DefaultExchange(context);
+            exchange.getMessage().setHeader("code", "an-authorization-code");
+            exchange.getMessage().setHeader("state", "not-the-issued-state");
+
+            OAuth oauth = bindTestOAuth(context);
+            
oauth.getOrCreateSession(exchange).putValue(OAuthSession.OAUTH_STATE, 
"the-issued-state");
+            new OAuthCodeFlowCallback().process(exchange);
+
+            assertEquals(400, 
exchange.getMessage().getHeader(Exchange.HTTP_RESPONSE_CODE));
+            assertEquals("Authorization state mismatch", 
exchange.getMessage().getBody());
+            assertTrue(exchange.isRouteStop());
+        }
+    }
+
+    @Test
+    void aCallbackWithNoStateAtAllStopsTheRoute() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            Exchange exchange = new DefaultExchange(context);
+            exchange.getMessage().setHeader("code", "an-authorization-code");
+
+            OAuth oauth = bindTestOAuth(context);
+            
oauth.getOrCreateSession(exchange).putValue(OAuthSession.OAUTH_STATE, 
"the-issued-state");
+            new OAuthCodeFlowCallback().process(exchange);
+
+            assertEquals(400, 
exchange.getMessage().getHeader(Exchange.HTTP_RESPONSE_CODE));
+            assertEquals("Authorization state mismatch", 
exchange.getMessage().getBody());
+            assertTrue(exchange.isRouteStop());
+        }
+    }
+
+    /**
+     * The state check runs before the authorization code is redeemed, so none 
of the abstract operations are reached -
+     * which is part of what these tests assert.
+     */
+    private static OAuth bindTestOAuth(DefaultCamelContext context) {
+        OAuth oauth = new OAuth() {
+            @Override
+            public void discoverOAuthConfig(CamelContext ctx) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public UserProfile authenticate(Credentials creds) {
+                throw new UnsupportedOperationException("the authorization 
code must not be redeemed");
+            }
+
+            @Override
+            public String buildLogoutRequestUrl(OAuthLogoutParams params) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String buildCodeFlowAuthRequestUrl(OAuthCodeFlowParams 
params) {
+                throw new UnsupportedOperationException();
+            }
+        };
+        context.getRegistry().bind(OAuth.class.getName(), oauth);
+        return oauth;
+    }
 }
diff --git 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
index 29b1271fa2c5..98f18bec1221 100644
--- 
a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
+++ 
b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityProcessor.java
@@ -130,24 +130,21 @@ public class ShiroSecurityProcessor extends 
DelegateAsyncProcessor {
     }
 
     private void authenticateUser(Subject currentUser, ShiroSecurityToken 
securityToken) {
-        boolean authenticated = currentUser.isAuthenticated();
-        boolean sameUser = 
securityToken.getUsername().equals(currentUser.getPrincipal());
-        LOG.trace("Authenticated: {}, same Username: {}", authenticated, 
sameUser);
+        // The login is not skipped when the thread-bound subject already 
carries the same principal name.
+        // A matching username says nothing about the password presented with 
this exchange, and with
+        // alwaysReauthenticate=false the subject is deliberately long-lived 
on a shared worker thread, so
+        // skipping would let a wrong password through for as long as that 
subject stays bound.
+        LOG.trace("Authenticating {} (subject currently authenticated: {})",
+                securityToken.getUsername(), currentUser.isAuthenticated());
 
-        if (!authenticated || !sameUser) {
-            UsernamePasswordToken token = new 
UsernamePasswordToken(securityToken.getUsername(), securityToken.getPassword());
-            if (policy.isAlwaysReauthenticate()) {
-                token.setRememberMe(false);
-            } else {
-                token.setRememberMe(true);
-            }
+        UsernamePasswordToken token = new 
UsernamePasswordToken(securityToken.getUsername(), securityToken.getPassword());
+        token.setRememberMe(!policy.isAlwaysReauthenticate());
 
-            try {
-                currentUser.login(token);
-                LOG.debug("Current user {} successfully authenticated", 
currentUser.getPrincipal());
-            } catch (AuthenticationException ae) {
-                throw new AuthenticationException("Authentication Failed.", 
ae.getCause());
-            }
+        try {
+            currentUser.login(token);
+            LOG.debug("Current user {} successfully authenticated", 
currentUser.getPrincipal());
+        } catch (AuthenticationException ae) {
+            throw new AuthenticationException("Authentication Failed.", 
ae.getCause());
         }
     }
 
diff --git 
a/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroAuthenticationCredentialAlwaysCheckedTest.java
 
b/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroAuthenticationCredentialAlwaysCheckedTest.java
new file mode 100644
index 000000000000..252d76e0cf2e
--- /dev/null
+++ 
b/components/camel-shiro/src/test/java/org/apache/camel/component/shiro/security/ShiroAuthenticationCredentialAlwaysCheckedTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.shiro.security;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.IncorrectCredentialsException;
+import org.apache.shiro.authc.LockedAccountException;
+import org.apache.shiro.authc.UnknownAccountException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * With {@code alwaysReauthenticate=false} the Shiro subject stays bound to 
the thread between exchanges, which is the
+ * point of the option. The password carried by each exchange still has to be 
verified: a username that happens to match
+ * the bound subject's principal is not evidence about the credentials 
presented this time.
+ */
+class ShiroAuthenticationCredentialAlwaysCheckedTest extends CamelTestSupport {
+
+    private static final byte[] TEST_KEY = 
"0123456789abcdef".getBytes(StandardCharsets.US_ASCII);
+
+    @EndpointInject("mock:success")
+    protected MockEndpoint successEndpoint;
+
+    @EndpointInject("mock:authenticationException")
+    protected MockEndpoint failureEndpoint;
+
+    @Test
+    void aWrongPasswordIsRejectedEvenAfterTheSameUserAuthenticated() throws 
Exception {
+        successEndpoint.expectedMessageCount(1);
+        failureEndpoint.expectedMessageCount(1);
+
+        // Authenticates and leaves the subject bound to this thread
+        template.send("direct:secureEndpoint", injector("ringo", "starr"));
+        // Same username, wrong password - must not ride the bound subject 
through
+        template.send("direct:secureEndpoint", injector("ringo", "not-starr"));
+
+        successEndpoint.assertIsSatisfied();
+        failureEndpoint.assertIsSatisfied();
+    }
+
+    private TestShiroSecurityTokenInjector injector(String user, String 
password) {
+        return new TestShiroSecurityTokenInjector(new ShiroSecurityToken(user, 
password), TEST_KEY);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        final ShiroSecurityPolicy securityPolicy
+                = new 
ShiroSecurityPolicy("./src/test/resources/securityconfig.ini", TEST_KEY, false);
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                onException(UnknownAccountException.class, 
IncorrectCredentialsException.class,
+                        LockedAccountException.class, 
AuthenticationException.class).to("mock:authenticationException");
+
+                
from("direct:secureEndpoint").policy(securityPolicy).to("mock:success");
+            }
+        };
+    }
+
+    private static class TestShiroSecurityTokenInjector extends 
ShiroSecurityTokenInjector {
+
+        TestShiroSecurityTokenInjector(ShiroSecurityToken shiroSecurityToken, 
byte[] bytes) {
+            super(shiroSecurityToken, bytes);
+        }
+
+        @Override
+        public void process(Exchange exchange) {
+            
exchange.getIn().setHeader(ShiroSecurityConstants.SHIRO_SECURITY_TOKEN, 
encrypt());
+            exchange.getIn().setBody("Beatle Mania");
+        }
+    }
+}
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pgp.json
 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pgp.json
index fda39c0a7b56..2548f5d96bb3 100644
--- 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pgp.json
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pgp.json
@@ -27,6 +27,7 @@
     "algorithm": { "index": 11, "kind": "attribute", "displayName": 
"Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Symmetric key encryption algorithm; possible 
values are defined in org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; for 
example 2 (= TRIPLE DES), 3 (= CAST5), 4 (= BLOWFISH), 6 (= DES), 7 (= 
AES_128). Only relevant for encrypting." },
     "compressionAlgorithm": { "index": 12, "kind": "attribute", "displayName": 
"Compression Algorithm", "group": "common", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": 
false, "secret": false, "description": "Compression algorithm; possible values 
are defined in org.bouncycastle.bcpg.CompressionAlgorithmTags; for example 0 (= 
UNCOMPRESSED), 1 (= ZIP), 2 (= ZLIB), 3 (= BZIP2). Only relevant for 
encrypting." },
     "hashAlgorithm": { "index": 13, "kind": "attribute", "displayName": "Hash 
Algorithm", "group": "common", "required": false, "type": "integer", 
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false, 
"secret": false, "description": "Signature hash algorithm; possible values are 
defined in org.bouncycastle.bcpg.HashAlgorithmTags; for example 2 (= SHA1), 8 
(= SHA256), 9 (= SHA384), 10 (= SHA512), 11 (=SHA224). Only relevant for 
signing." },
-    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "signatureVerificationOption": { "index": 14, "kind": "attribute", 
"displayName": "Signature Verification Option", "group": "common", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "Controls the behavior for 
verifying the signature during unmarshaling. There are 4 values possible: 
optional: The PGP message may or may not contain signatures; if it does contain 
signatures, then a signature ve [...]
+    "requireIntegrityProtection": { "index": 15, "kind": "attribute", 
"displayName": "Require Integrity Protection", "group": "common", "required": 
false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether a message must be integrity protected in order to be decrypted. The 
legacy symmetrically encrypted data packet carries no modification detection 
code, and the packet type is chosen  [...]
   }
 }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PGPDataFormat.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PGPDataFormat.java
index 9923cf7b06b2..a1098cec34dd 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PGPDataFormat.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PGPDataFormat.java
@@ -67,6 +67,14 @@ public class PGPDataFormat extends DataFormatDefinition {
     private String hashAlgorithm;
     @XmlAttribute
     private String signatureVerificationOption;
+    @XmlAttribute
+    @Metadata(javaType = "java.lang.Boolean", defaultValue = "true",
+              description = "Whether a message must be integrity protected in 
order to be decrypted. The legacy"
+                            + " symmetrically encrypted data packet carries no 
modification detection code, and the"
+                            + " packet type is chosen by whoever produced the 
message, so accepting it lets the sender"
+                            + " decide whether the integrity check applies. 
Set to false only to interoperate with a"
+                            + " sender that still emits the legacy packet.")
+    private String requireIntegrityProtection;
 
     public PGPDataFormat() {
         super("pgp");
@@ -88,6 +96,7 @@ public class PGPDataFormat extends DataFormatDefinition {
         this.compressionAlgorithm = source.compressionAlgorithm;
         this.hashAlgorithm = source.hashAlgorithm;
         this.signatureVerificationOption = source.signatureVerificationOption;
+        this.requireIntegrityProtection = source.requireIntegrityProtection;
     }
 
     private PGPDataFormat(Builder builder) {
@@ -106,6 +115,7 @@ public class PGPDataFormat extends DataFormatDefinition {
         this.compressionAlgorithm = builder.compressionAlgorithm;
         this.hashAlgorithm = builder.hashAlgorithm;
         this.signatureVerificationOption = builder.signatureVerificationOption;
+        this.requireIntegrityProtection = builder.requireIntegrityProtection;
     }
 
     @Override
@@ -291,6 +301,17 @@ public class PGPDataFormat extends DataFormatDefinition {
         this.signatureVerificationOption = signatureVerificationOption;
     }
 
+    public String getRequireIntegrityProtection() {
+        return requireIntegrityProtection;
+    }
+
+    /**
+     * Whether a message must be integrity protected in order to be decrypted. 
Defaults to true.
+     */
+    public void setRequireIntegrityProtection(String 
requireIntegrityProtection) {
+        this.requireIntegrityProtection = requireIntegrityProtection;
+    }
+
     /**
      * {@code Builder} is a specific builder for {@link PGPDataFormat}.
      */
@@ -311,6 +332,7 @@ public class PGPDataFormat extends DataFormatDefinition {
         private String compressionAlgorithm;
         private String hashAlgorithm;
         private String signatureVerificationOption;
+        private String requireIntegrityProtection;
 
         /**
          * User ID of the key in the PGP keyring used for signing (during 
encryption) or signature verification (during
@@ -495,6 +517,22 @@ public class PGPDataFormat extends DataFormatDefinition {
             return this;
         }
 
+        /**
+         * Whether a message must be integrity protected in order to be 
decrypted. Defaults to true.
+         */
+        public Builder requireIntegrityProtection(String 
requireIntegrityProtection) {
+            this.requireIntegrityProtection = requireIntegrityProtection;
+            return this;
+        }
+
+        /**
+         * Whether a message must be integrity protected in order to be 
decrypted. Defaults to true.
+         */
+        public Builder requireIntegrityProtection(boolean 
requireIntegrityProtection) {
+            this.requireIntegrityProtection = 
Boolean.toString(requireIntegrityProtection);
+            return this;
+        }
+
         @Override
         public PGPDataFormat end() {
             return new PGPDataFormat(this);
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PGPDataFormatReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PGPDataFormatReifier.java
index 0b97d0f9ebcc..de05a23a4b5b 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PGPDataFormatReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PGPDataFormatReifier.java
@@ -44,6 +44,7 @@ public class PGPDataFormatReifier extends 
DataFormatReifier<PGPDataFormat> {
         properties.put("compressionAlgorithm", 
definition.getCompressionAlgorithm());
         properties.put("hashAlgorithm", definition.getHashAlgorithm());
         properties.put("signatureVerificationOption", 
definition.getSignatureVerificationOption());
+        properties.put("requireIntegrityProtection", 
definition.getRequireIntegrityProtection());
     }
 
 }
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 598c58845a34..d5df20f56136 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -2106,6 +2106,7 @@ public class ModelParser extends BaseParser {
                 case "keyUserid": def.setKeyUserid(val); yield true;
                 case "password": def.setPassword(val); yield true;
                 case "provider": def.setProvider(val); yield true;
+                case "requireIntegrityProtection": 
def.setRequireIntegrityProtection(val); yield true;
                 case "signatureKeyFileName": def.setSignatureKeyFileName(val); 
yield true;
                 case "signatureKeyRing": def.setSignatureKeyRing(val); yield 
true;
                 case "signatureKeyUserid": def.setSignatureKeyUserid(val); 
yield true;
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 3694b0e391f5..88484439eaf3 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2820,6 +2820,7 @@ public class ModelWriter extends BaseWriter {
         doWriteAttribute("signatureKeyRing", def.getSignatureKeyRing(), null);
         doWriteAttribute("signatureVerificationOption", 
def.getSignatureVerificationOption(), null);
         doWriteAttribute("keyFileName", def.getKeyFileName(), null);
+        doWriteAttribute("requireIntegrityProtection", 
def.getRequireIntegrityProtection(), "true");
         doWriteAttribute("signatureKeyUserid", def.getSignatureKeyUserid(), 
null);
         doWriteAttribute("integrity", def.getIntegrity(), "true");
         doWriteAttribute("password", def.getPassword(), null);
diff --git 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index bfe9b4979e8c..71b9d5772d64 100644
--- 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2820,6 +2820,7 @@ public class ModelWriter extends BaseWriter {
         doWriteAttribute("signatureKeyRing", def.getSignatureKeyRing(), null);
         doWriteAttribute("signatureVerificationOption", 
def.getSignatureVerificationOption(), null);
         doWriteAttribute("keyFileName", def.getKeyFileName(), null);
+        doWriteAttribute("requireIntegrityProtection", 
def.getRequireIntegrityProtection(), "true");
         doWriteAttribute("signatureKeyUserid", def.getSignatureKeyUserid(), 
null);
         doWriteAttribute("integrity", def.getIntegrity(), "true");
         doWriteAttribute("password", def.getPassword(), null);
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/MllpComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/MllpComponentBuilderFactory.java
index 024a459c66d8..9f5ef2ae9155 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/MllpComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/MllpComponentBuilderFactory.java
@@ -495,7 +495,7 @@ public interface MllpComponentBuilderFactory {
          * 
          * The option is a: &lt;code&gt;java.lang.Boolean&lt;/code&gt; type.
          * 
-         * Default: true
+         * Default: false
          * Group: advanced
          * 
          * @param logPhi the value to set
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index b682236ce98c..1097656e5e55 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -11783,6 +11783,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     @YamlProperty(name = "keyUserid", type = "string", 
description = "The user ID of the key in the PGP keyring used during 
encryption. Can also be only a part of a user ID. For example, if the user ID 
is Test User then you can use the part Test User or to address the user ID.", 
displayName = "Key Userid"),
                     @YamlProperty(name = "password", type = "string", 
description = "Password used when opening the private key (not used for 
encryption).", displayName = "Password"),
                     @YamlProperty(name = "provider", type = "string", 
description = "Java Cryptography Extension (JCE) provider, default is Bouncy 
Castle (BC). Alternatively you can use, for example, the IAIK JCE provider; in 
this case the provider must be registered beforehand and the Bouncy Castle 
provider must not be registered beforehand. The Sun JCE provider does not 
work.", displayName = "Provider"),
+                    @YamlProperty(name = "requireIntegrityProtection", type = 
"boolean", defaultValue = "true", description = "Whether a message must be 
integrity protected in order to be decrypted. The legacy symmetrically 
encrypted data packet carries no modification detection code, and the packet 
type is chosen by whoever produced the message, so accepting it lets the sender 
decide whether the integrity check applies. Set to false only to interoperate 
with a sender that still emits th [...]
                     @YamlProperty(name = "signatureKeyFileName", type = 
"string", description = "Filename of the keyring to use for signing (during 
encryption) or for signature verification (during decryption); must be 
accessible as a classpath resource (but you can specify a location in the file 
system by using the file: prefix).", displayName = "Signature Key File Name"),
                     @YamlProperty(name = "signatureKeyRing", type = "string", 
description = "Keyring used for signing/verifying as byte array. You can not 
set the signatureKeyFileName and signatureKeyRing at the same time.", 
displayName = "Signature Key Ring"),
                     @YamlProperty(name = "signatureKeyUserid", type = 
"string", description = "User ID of the key in the PGP keyring used for signing 
(during encryption) or signature verification (during decryption). During the 
signature verification process the specified User ID restricts the public keys 
from the public keyring which can be used for the verification. If no User ID 
is specified for the signature verficiation then any public key in the public 
keyring can be used for the v [...]
@@ -11855,6 +11856,11 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     target.setProvider(val);
                     break;
                 }
+                case "requireIntegrityProtection": {
+                    String val = asText(node);
+                    target.setRequireIntegrityProtection(val);
+                    break;
+                }
                 case "signatureKeyFileName": {
                     String val = asText(node);
                     target.setSignatureKeyFileName(val);
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index a2d5e3f5b63f..c112ffe65f73 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -12495,6 +12495,12 @@
             "title" : "Provider",
             "description" : "Java Cryptography Extension (JCE) provider, 
default is Bouncy Castle (BC). Alternatively you can use, for example, the IAIK 
JCE provider; in this case the provider must be registered beforehand and the 
Bouncy Castle provider must not be registered beforehand. The Sun JCE provider 
does not work."
           },
+          "requireIntegrityProtection" : {
+            "type" : "boolean",
+            "title" : "Require Integrity Protection",
+            "description" : "Whether a message must be integrity protected in 
order to be decrypted. The legacy symmetrically encrypted data packet carries 
no modification detection code, and the packet type is chosen by whoever 
produced the message, so accepting it lets the sender decide whether the 
integrity check applies. Set to false only to interoperate with a sender that 
still emits the legacy packet.",
+            "default" : true
+          },
           "signatureKeyFileName" : {
             "type" : "string",
             "title" : "Signature Key File Name",

Reply via email to