[ 
https://issues.apache.org/jira/browse/CAMEL-23066?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen updated CAMEL-23066:
--------------------------------
    Summary: camel-as2 - AS2Utils.createMessageId() produces duplicate 
Message-IDs after JVM restart (nanoTime-based)  (was: 
AS2Utils.createMessageId() produces duplicate Message-IDs after JVM restart 
(nanoTime-based))

> camel-as2 - AS2Utils.createMessageId() produces duplicate Message-IDs after 
> JVM restart (nanoTime-based)
> --------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-23066
>                 URL: https://issues.apache.org/jira/browse/CAMEL-23066
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-as2
>    Affects Versions: 4.18.0
>            Reporter: Reto Peter
>            Priority: Minor
>         Attachments: AS2Utils.java
>
>
> {panel:title=Problem}
>   \{{AS2Utils.createMessageId()}} generates Message-IDs using 
> \{{System.nanoTime()}} combined with a random number. \{{nanoTime()}} is 
> relative to an arbitrary JVM start time and resets on every JVM restart. This 
> produces duplicate Message-IDs that violate the AS2
>   specification (RFC 4130) requirement for globally unique message 
> identifiers. Receiving AS2 servers that use the Message-ID as a primary key 
> (e.g., OpenAS2) reject the message with a duplicate key violation.
>   \{panel}
>   h3. Root Cause
>   The current implementation in \{{AS2Utils.java}}:
>   \{code:java|title=Current code in AS2Utils.java (Camel 4.18.0)}
>   private static SecureRandom generator = new SecureRandom();
>   public static String createMessageId(String fqdn) {
>       return "<" + Long.toString(System.nanoTime(), 36) + "."
>            + Long.toString(generator.nextLong(), 36) + "@" + fqdn + ">";
>   }
>   \{code}
>   The problem is \{{System.nanoTime()}}:
>   - It returns a value relative to an arbitrary origin that resets on each 
> JVM start
>   - After a server restart, the first few messages will have the same 
> nanoTime range as messages sent during a previous JVM lifetime
>   - Combined with the random part, there is a significant collision 
> probability across restarts because the nanoTime part provides the 
> "sequential" component
>   Example: Server sends messages, restarts, sends messages again. The 
> nanoTime values from the second run overlap with values from the first run. 
> If the random part happens to be similar, duplicate Message-IDs are produced.
>   h3. Steps to Reproduce
>   Configure a Camel AS2 endpoint and send several messages to a partner
>   Restart the JVM (e.g., application server restart, deployment)
>   Send messages again immediately after restart
>   Observe: some Message-IDs may collide with IDs generated in the previous 
> JVM lifetime
>   Partners that store Message-IDs as unique keys (e.g., OpenAS2) reject the 
> message with a primary key violation
>   h3. Proposed Fix
>   Replace the \{{nanoTime}}-based generation with UUID v7 (RFC 9562), which 
> embeds a 48-bit millisecond wall-clock timestamp plus 74 bits of 
> cryptographic randomness. This guarantees uniqueness across JVM restarts and 
> provides lexicographic sortability by
>   creation time.
>   \{code:diff}
>   - private static SecureRandom generator = new SecureRandom();
>   - private static final SecureRandom RANDOM = new SecureRandom();
>   - public static String createMessageId(String fqdn) {
>      return "<" + Long.toString(System.nanoTime(), 36) + "."
>           + Long.toString(generator.nextLong(), 36) + "@" + fqdn + ">";
>      // UUID v7 (RFC 9562): 48-bit unix_ts_ms + 4-bit version + 12-bit random 
> + 2-bit variant + 62-bit random
>      long timestamp = System.currentTimeMillis();
>      long msb = ((timestamp & 0xFFFF_FFFF_FFFFL) << 16) // 48-bit timestamp → 
> bits 63-16
>               | (0x7L << 12)                              // version 7        
> → bits 15-12
>               | (RANDOM.nextLong() & 0xFFFL);             // rand_a 12 bits   
> → bits 11-0
>      long lsb = (RANDOM.nextLong() & 0x3FFF_FFFF_FFFF_FFFFL) // rand_b 62 
> bits → bits 61-0
>               | 0x8000_0000_0000_0000L;                        // variant 10  
>    → bits 63-62
>      UUID uuidV7 = new UUID(msb, lsb);
>      return "<" + uuidV7 + "@" + fqdn + ">";
>   -    }
>   \{code}
>   h4. Additional import needed
>   \{code:java}
>   import java.util.UUID;
>   \{code}
>   h3. Why UUID v7
>   
> ┌────────────────────────────┬──────────────────────────────┬──────────────────────┬────────────────────────┐
>   │                            │      nanoTime (current)      │ UUID v4 
> (randomUUID) │ UUID v7 (proposed fix) │
>   
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
>   │ Unique across JVM restarts │ No                           │ Yes           
>        │ Yes                    │
>   
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
>   │ Sortable by creation time  │ No                           │ No            
>        │ Yes                    │
>   
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
>   │ Cryptographically random   │ Partially (random part only) │ Yes           
>        │ Yes (74 random bits)   │
>   
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
>   │ RFC standard               │ No                           │ RFC 9562      
>        │ RFC 9562               │
>   
> └────────────────────────────┴──────────────────────────────┴──────────────────────┴────────────────────────┘
>   Note: \{{UUID.randomUUID()}} (v4) would also solve the uniqueness problem. 
> UUID v7 is preferred because it additionally provides time-based sortability, 
> which is useful for log analysis and debugging. Both are acceptable solutions.
>   h3. Example Output
>   Before (nanoTime-based, restarts cause collisions):
>   \{code}
>   [email protected]
>   \{code}
>   After (UUID v7, globally unique):
>   \{code}
>   [email protected]
>   \{code}
>   h3. Test Scenario
>   Tested with OpenAS2 as the receiving partner. Before the fix, after a JVM 
> restart OpenAS2 rejected messages with duplicate Message-ID errors. After the 
> fix, all Message-IDs are unique across restarts.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to