|
| 1 | +# EXPath Crypto Module for eXist-db |
| 2 | + |
| 3 | +A standalone XAR package implementing the [EXPath Cryptographic Module](http://expath.org/spec/crypto) for eXist-db 7.0+. Provides cryptographic hashing, HMAC authentication, symmetric encryption/decryption, and XML digital signatures using Java's built-in JCE (Java Cryptography Extension) — no external dependencies. |
| 4 | + |
| 5 | +## Install |
| 6 | + |
| 7 | +Download the `.xar` from [Releases](https://github.com/joewiz/exist-crypto/releases) and install with the eXist-db Package Manager or the `xst` CLI: |
| 8 | + |
| 9 | +```bash |
| 10 | +xst package install exist-crypto-0.9.0-SNAPSHOT.xar |
| 11 | +``` |
| 12 | + |
| 13 | +## Functions |
| 14 | + |
| 15 | +| Function | Description | |
| 16 | +|----------|-------------| |
| 17 | +| `crypto:hash($data, $algorithm)` | Cryptographic hash with Base64 output | |
| 18 | +| `crypto:hash($data, $algorithm, $encoding)` | Cryptographic hash with specified encoding (`base64` or `hex`) | |
| 19 | +| `crypto:hmac($data, $key, $algorithm)` | HMAC with Base64 output | |
| 20 | +| `crypto:hmac($data, $key, $algorithm, $encoding)` | HMAC with specified encoding (`base64` or `hex`) | |
| 21 | +| `crypto:encrypt($data, $type, $key, $algorithm)` | Symmetric encryption (AES or DES) | |
| 22 | +| `crypto:decrypt($data, $type, $key, $algorithm)` | Symmetric decryption | |
| 23 | +| `crypto:generate-signature($data, $c14n, $digest, $sig, $prefix, $type)` | Generate XML digital signature | |
| 24 | +| `crypto:validate-signature($data)` | Validate XML digital signature | |
| 25 | + |
| 26 | +**Module namespace:** `http://expath.org/ns/crypto` |
| 27 | + |
| 28 | +### Hash |
| 29 | + |
| 30 | +```xquery |
| 31 | +import module namespace crypto = "http://expath.org/ns/crypto"; |
| 32 | +
|
| 33 | +(: Base64 output (default) :) |
| 34 | +crypto:hash("data", "SHA-256") |
| 35 | +
|
| 36 | +(: Hex output :) |
| 37 | +crypto:hash("data", "SHA-256", "hex") |
| 38 | +``` |
| 39 | + |
| 40 | +**Algorithms:** MD5, SHA-1, SHA-256, SHA-384, SHA-512 |
| 41 | + |
| 42 | +For new code, prefer `fn:hash()` (XQuery 4.0) or `util:hash()`. `crypto:hash` is provided for backward compatibility with the EXPath spec and cross-engine portability (BaseX also implements it). |
| 43 | + |
| 44 | +### HMAC |
| 45 | + |
| 46 | +```xquery |
| 47 | +import module namespace crypto = "http://expath.org/ns/crypto"; |
| 48 | +
|
| 49 | +(: Base64 output (default) :) |
| 50 | +crypto:hmac("data", "secret-key", "SHA256") |
| 51 | +
|
| 52 | +(: Hex output :) |
| 53 | +crypto:hmac("data", "secret-key", "SHA256", "hex") |
| 54 | +``` |
| 55 | + |
| 56 | +**Algorithms:** MD5, SHA1, SHA256, SHA384, SHA512 |
| 57 | + |
| 58 | +### Symmetric Encryption |
| 59 | + |
| 60 | +```xquery |
| 61 | +import module namespace crypto = "http://expath.org/ns/crypto"; |
| 62 | +
|
| 63 | +let $key := "0123456789abcdef" (: 16 bytes = AES-128 :) |
| 64 | +let $encrypted := crypto:encrypt("secret message", "symmetric", $key, "AES") |
| 65 | +return crypto:decrypt($encrypted, "symmetric", $key, "AES") |
| 66 | +``` |
| 67 | + |
| 68 | +**Algorithms:** AES (16/24/32-byte key), DES (8-byte key) |
| 69 | + |
| 70 | +A random initialization vector (IV) is automatically generated and prepended to the ciphertext. |
| 71 | + |
| 72 | +### XML Digital Signatures |
| 73 | + |
| 74 | +```xquery |
| 75 | +import module namespace crypto = "http://expath.org/ns/crypto"; |
| 76 | +
|
| 77 | +let $doc := <order id="123"><total>99.99</total></order> |
| 78 | +let $signed := crypto:generate-signature( |
| 79 | + $doc, "inclusive", "SHA256", "RSA_SHA256", "dsig", "enveloped") |
| 80 | +return crypto:validate-signature($signed) |
| 81 | +``` |
| 82 | + |
| 83 | +## Relationship to expath-crypto-module |
| 84 | + |
| 85 | +This package is the **successor** to [`expath-crypto-module`](https://github.com/eXist-db/expath-crypto-module) (originally by Claudius Teodorescu, last updated 2020). Both implement the same [EXPath Cryptographic Module](http://expath.org/spec/crypto) specification and share the same module namespace and `crypto:` prefix — so existing XQuery code using `crypto:` functions will work with this package. However, there are some behavior differences (see migration guide below). |
| 86 | + |
| 87 | +The two packages **cannot be installed simultaneously** because they register the same module namespace (`http://expath.org/ns/crypto`). A `pre-install.xq` script automatically detects and removes the old package when you install this one. |
| 88 | + |
| 89 | +### Why switch? |
| 90 | + |
| 91 | +| | Predecessor (`expath-crypto-module`) | Successor (`exist-crypto`) | |
| 92 | +|---|---|---| |
| 93 | +| **eXist-db compatibility** | 5.x–6.x (broken on 7.x due to Type constant renumbering) | 7.0+ | |
| 94 | +| **Java version** | Java 8+ | Java 21+ | |
| 95 | +| **Dependencies** | External `crypto-java` library (ro.kuberam) | Zero — uses Java's built-in JCE | |
| 96 | +| **Parent POM** | `exist-apps-parent` 1.11.0 | `exist-apps-parent` 2.0.0 | |
| 97 | +| **`crypto:hash`** | Yes (2–3 arity) | Yes (2–3 arity, compatible) | |
| 98 | + |
| 99 | +### Upgrade steps |
| 100 | + |
| 101 | +1. **Install the new package** — the `pre-install.xq` script automatically detects and removes the old `expath-crypto-module`: |
| 102 | + |
| 103 | + ```bash |
| 104 | + xst package install exist-crypto-0.9.0-SNAPSHOT.xar |
| 105 | + ``` |
| 106 | + |
| 107 | + To remove the old package manually beforehand: |
| 108 | + |
| 109 | + ```bash |
| 110 | + xst package remove http://expath.org/ns/crypto |
| 111 | + ``` |
| 112 | + |
| 113 | +2. **Review your XQuery code** — see the migration guide below. Most code will work without changes. |
| 114 | + |
| 115 | +### Migration guide |
| 116 | + |
| 117 | +#### No changes needed |
| 118 | + |
| 119 | +The module namespace is identical, so **import statements require no changes**: |
| 120 | + |
| 121 | +```xquery |
| 122 | +import module namespace crypto = "http://expath.org/ns/crypto"; |
| 123 | +``` |
| 124 | + |
| 125 | +Calls to `crypto:hash`, `crypto:hmac`, and `crypto:validate-signature` are compatible as-is for the most common usage patterns. |
| 126 | + |
| 127 | +#### `crypto:hmac` — return type changed |
| 128 | + |
| 129 | +| | Predecessor | Successor | |
| 130 | +|---|---|---| |
| 131 | +| **Parameter types** | `$data as xs:atomic*`, `$key as xs:atomic*` | `$data as xs:string`, `$key as xs:string` | |
| 132 | +| **Return type** | `xs:byte*` | `xs:string` | |
| 133 | + |
| 134 | +The predecessor accepted binary types and returned a byte sequence. The successor accepts strings and returns a string (Base64 or hex). If your code passes `xs:string` arguments (the common case), no changes are needed. |
| 135 | + |
| 136 | +**If you passed binary data:** Convert to string first with `util:binary-to-string()`. |
| 137 | + |
| 138 | +#### `crypto:encrypt` / `crypto:decrypt` — IV handling changed |
| 139 | + |
| 140 | +| | Predecessor | Successor | |
| 141 | +|---|---|---| |
| 142 | +| **IV parameter** | Optional 5th argument (`xs:base64Binary`) | Automatic (random IV prepended to ciphertext) | |
| 143 | +| **Provider parameter** | Optional 6th argument | Not supported | |
| 144 | +| **Arity** | 4–6 | 4 | |
| 145 | + |
| 146 | +The successor always generates a random IV and prepends it to the ciphertext. The decrypt function extracts the IV automatically. This is more secure than reusing IVs but means **ciphertext from the predecessor cannot be decrypted by the successor** (and vice versa) unless the IV handling is compatible. |
| 147 | + |
| 148 | +**If you stored encrypted data:** You will need to re-encrypt data during migration. Decrypt with the predecessor first, then re-encrypt with this package. |
| 149 | + |
| 150 | +**If you used a custom provider:** Remove the provider argument. Java's default JCE provider is used. |
| 151 | + |
| 152 | +#### `crypto:generate-signature` — subset of arities |
| 153 | + |
| 154 | +| | Predecessor | Successor | |
| 155 | +|---|---|---| |
| 156 | +| **Arities** | 6, 7 (XPath), 8 (certificate), 3 (private key) | 6 | |
| 157 | +| **Signature algorithms** | RSA\_SHA1, DSA\_SHA1 | RSA\_SHA1, DSA\_SHA1, RSA\_SHA256 | |
| 158 | + |
| 159 | +The 6-argument form is compatible. The XPath subsetting (7-arg), certificate (8-arg), and private key (3-arg) variants are not yet implemented. |
| 160 | + |
| 161 | +**If you used the 6-argument form:** No changes needed. |
| 162 | + |
| 163 | +**If you used XPath subsetting or certificates:** Not yet available. File an issue if you need them. |
| 164 | + |
| 165 | +#### `crypto:hash` — return type changed |
| 166 | + |
| 167 | +| | Predecessor | Successor | |
| 168 | +|---|---|---| |
| 169 | +| **Parameter types** | `$data as item()*` | `$data as item()` | |
| 170 | +| **Return type** | `xs:byte*` | `xs:string` | |
| 171 | + |
| 172 | +The predecessor returned a byte sequence. The successor returns a string (Base64 or hex encoded). The function signatures and algorithm names are otherwise compatible. |
| 173 | + |
| 174 | +For new code, prefer `fn:hash()` (XQuery 4.0) or `util:hash()`. `crypto:hash` is provided for EXPath spec conformance and backward compatibility (BaseX also implements it). |
| 175 | + |
| 176 | +### Package identity |
| 177 | + |
| 178 | +| Property | Predecessor | Successor | |
| 179 | +|---|---|---| |
| 180 | +| Package name (URI) | `http://expath.org/ns/crypto` | `http://exist-db.org/pkg/crypto` | |
| 181 | +| Package abbreviation | `crypto` | `exist-crypto` | |
| 182 | +| Module namespace | `http://expath.org/ns/crypto` | `http://expath.org/ns/crypto` | |
| 183 | +| XQuery prefix | `crypto:` | `crypto:` | |
| 184 | +| Maven groupId | `org.exist-db.xquery.extensions.expath` | `org.exist-db` | |
| 185 | +| Maven artifactId | `expath-crypto-module` | `exist-crypto` | |
| 186 | +| Java package | `org.expath.exist.crypto` | `org.exist.xquery.modules.crypto` | |
| 187 | + |
| 188 | +The module namespace and `crypto:` prefix are identical — no XQuery import changes needed. The package abbreviations differ (`crypto` vs `exist-crypto`), but the `pre-install.xq` script handles the transition automatically. |
| 189 | + |
| 190 | +## Build |
| 191 | + |
| 192 | +```bash |
| 193 | +JAVA_HOME=/path/to/java-21 mvn clean package -DskipTests |
| 194 | +``` |
| 195 | + |
| 196 | +Run integration tests (requires exist-core 7.0.0-SNAPSHOT in your local Maven repo): |
| 197 | + |
| 198 | +```bash |
| 199 | +mvn test -Pintegration-tests |
| 200 | +``` |
| 201 | + |
| 202 | +## License |
| 203 | + |
| 204 | +[GNU Lesser General Public License v2.1](https://opensource.org/licenses/LGPL-2.1) |
0 commit comments