<a href="https://www.buymeacoffee.com/eduardoeljaiek" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 35px !important;width: 125px !important;" ></a>
**Last Update**: 19.04.2026
***
***JMaskify*** is an open-source Java library designed to safeguard sensitive data with versatile and customizable masking techniques. Whether your application handles personal, financial, or confidential information, _JMaskify_ ensures its security through intuitive APIs and advanced masking strategies.
## Key Features
- **Versatile Masking**: Supports fixed-length anonymization, Base64 encoding, Debit/Credit Card Numbers, JSON, and multiline text masking.
- **Flexible API**: Easily adaptable for use cases involving [[#2. JSON Masking | JSON structures]] and [[#3. Multiline Text Masking | multiline text]].
- **Open Source**: MIT-licensed to encourage collaboration and transparency.
## Project Requirements
To work with *JMaskify*, ensure the following tools and dependencies are installed:
- **JDK**: Version 17 or higher
- **Build Tool**: Maven or Gradle
## Dependencies
*JMaskify* leverages the following key libraries:
- **Jackson**: JSON processing (`jackson-databind`)
- **Apache Commons Codec**: Encoding utilities
- **SLF4J**: Logging framework
## Getting Started
### Installation
JMaskify is available on Maven Central. Add the dependency to your project:
```xml
<dependency>
<groupId>io.github.ehayik</groupId>
<artifactId>jmaskify</artifactId>
<version>1.1.0</version>
</dependency>
```
This includes [jackson-core](https://github.com/FasterXML/jackson-core) and [jackson-databind](https://github.com/FasterXML/jackson-databind) for JSON processing.
All dependencies and versions are managed in the project's `pom.xml`
### Basic Usage
#### 1. Simple String Masking
The quickest way to mask sensitive data:
```java
// Mask a credit card number
var creditCardNumber = "4289-3874-8064-8976";
var masked = Masker.creditCard().apply(creditCardNumber);
// Result: "****-****-****-8976"
// Mask an email address
var email = "
[email protected]";
var maskedEmail = Masker.fixedLength().apply(email);
// Result: "****************"
```
#### 2. JSON Masking
For masking fields within JSON objects:
```java
var json = """
{
"name": "John Doe",
"age": 30,
"contactInfo": {
"email": "
[email protected]",
"phone": "123-456-7890"
}, "creditCard": "1234-5678-9012-3456"
}
""";
// Create a JsonMasker instance
var masker = Masker.json()
.prettify(true)
.withProperty("email") // Default fixed pattern masking
.withProperty("phone", Masker.base64())
.withProperty("creditCard", Masker.creditCard('X'))
.withProperty("name", Masker.delegate(value -> "■■■■■■"));
// Apply masking
var maskedJson = masker.apply(json);
/*
Result:
{
"name": "■■■■■■",
"age": 30,
"contactInfo": {
"email": "***************",
"phone": "MTIzLTQ1Ni03ODkw"
},
"creditCard": "XXXX-XXXX-XXXX-3456"
}
*/
```
> [!INFO]
> Non-string values (numbers, booleans, nulls) are preserved without masking.
##### Masking string values within a JSON array
*JMaskify* provides powerful capabilities for masking string values within JSON arrays. Here are examples of different array masking scenarios:
###### Example 1: Simple Array of Strings
When you need to mask an array of sensitive string values, such as credit card numbers:
```java
var json = """
{
"name": "John Doe",
"creditCards": [
"1234-5678-9012-3456",
"4289-3874-8064-8976"
]
}
""";
var maskedJson = Masker.json()
.withProperty("creditCards", Masker.creditCard('X'))
.apply(json);
/* Result:
{
"name": "John Doe",
"creditCards": [
"XXXX-XXXX-XXXX-3456",
"XXXX-XXXX-XXXX-8976"
]
}
*/
```
###### Example 2: Arrays with Mixed Value Types
*JMaskify* can handle arrays containing a mix of different value types, masking only the string values:
```java
var json = """
{
"name": "John Doe",
"mixedArray": [
"sensitive-string-data",
42,
true,
null,
{"nestedKey": "nestedValue"},
["nested", "array"]
]
}
""";
var maskedJson = Masker.json()
.withProperty("mixedArray", Masker.fixedLength())
.apply(json);
/* Result:
{
"name": "John Doe",
"mixedArray": [
"*********************",
42,
true,
null,
{"nestedKey": "***********"},
["******", "*****"]
]
}
*/
```
###### Example 3: Nested Arrays
*JMaskify* handles nested arrays with specific masking behavior:
```java
var json = """
{
"name": "John Doe",
"nestedArrays": [
["sensitive-outer-inner", "another-value"],
42,
["not-masked-1", "not-masked-2"]
]
}
""";
var maskedJson = Masker.json()
.withProperty("nestedArrays", Masker.fixedLength())
.apply(json);
/* Result:
{
"name": "John Doe",
"nestedArrays": [
["*********************", "*************"],
42,
["************", "************"]
]
}
*/
```
> [!INFO]
> All string values within nested arrays are masked consistently, regardless of their position or nesting level.
#### 3. Multiline Text Masking
When working with log files or other text that spans multiple lines, you can use multiline text masking to identify and mask patterns:
```java
var logContent = """
2023-05-15 INFO User
[email protected] logged in
2023-05-15 INFO IP Address: 192.168.1.1
""";
// Create a MultilinePatternMasker instance
var masker = Masker.multilinePattern()
.withMaskPattern("(\\d+\\.\\d+\\.\\d+\\.\\d+)");
// Apply masking
var maskedContent = masker.apply(logContent);
// Result:
// 2023-05-15 INFO User
[email protected] logged in
// 2023-05-15 INFO IP Address: **********
```
## Advanced Usage
Once you're comfortable with the basic masking operations, *JMaskify* offers more sophisticated features to handle complex masking requirements.
### Advanced Masker Configuration
*JMaskify* provides several ways to customize the behavior of its core maskers.
#### 1. FixedLengthMasker Configuration
The `FixedLengthMasker` can be configured to preserve parts of the input, ignore specific characters, or use a custom substitution character.
```java
// Configure a masker with prefix/suffix preservation and custom substitution
var masker = Masker.fixedLength(10) // Resulting masked part will be 10 characters
.withSubstitution('#') // Use '#' instead of '*'
.preservePrefix(2) // Keep first 2 characters
.preserveSuffix(2) // Keep last 2 characters
.build();
var result = masker.apply("1234567890123");
// Result: "12##########23" (prefix "12" + 10 '#' + suffix "23")
// Configure a masker to ignore specific characters
var maskerWithIgnore = Masker.fixedLength()
.ignore('-') // Do not mask '-' characters
.preservePrefix(3); // Keep first 3 characters
.build();
var resultWithIgnore = maskerWithIgnore.apply("123-456-789");
// Result: "123-###-###" (fixedLength is ignored when ignore() is used)
```
> [!INFO]
> When `ignore(char)` is configured on `FixedLengthMasker`, `fixedLength` is ignored and the output length follows the input value while the ignored characters are preserved.
#### 2. MultilinePatternMasker Configuration
You can assign specific masking strategies to different patterns within a `MultilinePatternMasker`.
```java
var masker = Masker.multilinePattern()
// Specific masker for CC
.withMaskPattern("(\\d{4}-\\d{4}-\\d{4}-\\d{4})", Masker.creditCard())
.withMaskPattern("(\\d+\\.\\d+\\.\\d+\\.\\d+)") // Default masking for IP
.withSubstitution('X');
String content = "CC: 1234-5678-9012-3456, IP: 192.168.1.1";
var result = masker.apply(content);
// Result: "CC: XXXX-XXXX-XXXX-3456, IP: XXXXXXXXXXX"
```
### Custom Masking Strategies
Create your own masking strategies for specialized requirements:
```java
var masker = Masker.delegate((String input) -> {
if (input == null || input.length() <= 2) {
return input;
}
var first = input.charAt(0);
var last = input.charAt(input.length() - 1);
return first +
"*".repeat(input.length() - 2) +
last;});
var maskedName = masker.apply("Johnson");
// Result: "J*****n"
```
### Combining Masking Strategies
For complex scenarios, combine different masking approaches:
```java
var json = """
{
"name": "John Doe",
"logs": [
"2023-05-15 INFO User
[email protected] logged in",
"2023-05-15 INFO IP Address: 192.168.1.1"
]
}
""";
var jsonMasker = Masker.json()
.withProperty("name", Masker.delegate(value -> "■■■■■■"))
.build();
var multilineMasker = Masker.multilinePattern()
.withMaskPattern("(\\d+\\.\\d+\\.\\d+\\.\\d+)")
.build();
var maskedContent = jsonMasker
.andThen(multilineMasker)
.andThen(Masker.base64())
.apply(json);
// Result applies all masking strategies in sequence
```
### Logging
JMaskify uses the [SLF4J](https://slf4j.org) logging facade, allowing integration with popular logging frameworks such as [Logback](https://logback.qos.ch), [Log4j](https://logging.apache.org/log4j/2.x/index.html), [tinylog](https://tinylog.org/v2/), or [Java Utils Logging](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html).
Refer to your logging framework's documentation to set the `DEBUG` log level for the package `io.github.ehayik.jmaskify`.
#### Logback Configuration Example
Add the following configuration to your `logback.xml` file to enable debug logs:
```xml
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="io.github.ehayik.jmaskify" level="DEBUG" />
</configuration>
```
### Handling Edge Cases
JMaskify provides robust handling for invalid inputs. For instance, attempting to mask invalid JSON results in exceptions with clear error messages:
```java
// Create a masker for email fields
var masker = Masker.json().withProperty("email").build();
// When applying to invalid JSON, a MaskingException will be thrown
// with the message "Failed to mask JSON content"
// masker.apply("///"); // This would throw MaskingException
```
### Masker Mutation
JMaskify allows you to create new masker variations from existing ones using the `mutate()` method. This is particularly useful when you need multiple maskers that share a common base configuration but differ in specific property or pattern settings.
The `mutate()` method returns a `MaskerBuilder` pre-configured with the settings from the original masker, which you can then further customize and build.
```java
// Define a base JSON masker with common settings
var baseMasker = Masker.json()
.prettify(true)
.withProperty("email")
.withProperty("phone", Masker.base64())
.build();
// Create a variation for a specific use case
var userProfileMasker = baseMasker.mutate()
.withProperty("creditCard", Masker.creditCard('X'))
.withProperty("name", Masker.delegate(value -> "■■■■■■"))
.build();
// The userProfileMasker now masks 'email', 'phone', 'creditCard', and 'name'
// while retaining the 'prettify(true)' setting from the base masker.
```
Mutation is supported for both `JsonMasker` and `MultilinePatternMasker`.
### Security Considerations
**JMaskify is designed to enhance data protection by obfuscating sensitive information. Developers must be aware of how different strategies affect security.**
- **Purpose**: The primary goal is to prevent accidental exposure of sensitive data (PII, PCI, etc.) in logs, reports, or non-secure storage.
- **Irreversibility**:
- Most core maskers (`FixedLengthMasker`, `CreditCardMasker`, `JsonMasker`, `MultilinePatternMasker`) are **irreversible**—the original data cannot be reconstructed from the masked output.
- **Base64 Encoding**: `Base64Masker` is **reversible** and should only be used for debugging or non-security-critical obfuscation where visibility is the only concern, not confidentiality.
- **In-Memory Safety**: Maskers operate on `String` objects, which are immutable in Java and may persist in memory. For extremely sensitive secrets (like passwords), consider using byte arrays or char arrays outside of this library's typical use cases.
- **Strategy Selection**:
- Use `CreditCardMasker` for financial data to keep the necessary digits for identification while hiding the rest.
- Use `FixedLengthMasker` to hide data completely while preserving the original field's presence/length for context.
- **JSON Security**: `JsonMasker` uses a streaming API to avoid loading large JSON trees into memory where possible, though the final masked output will still be a string.