Skip to content

Commit 078bcce

Browse files
authored
refactor: remove private/public key from commons and use typed keys expected by the algorithms (#243)
Break inheritance with Key superclass as the byte[] is all we need. The secret key is 32 of length and by using a type for SecretKey we don't need to check as the code does not allow to pass a SecretKey instance to a PrivateKey instance. Typing to the rescue. Public/private key define own types in each version.
1 parent a28d19a commit 078bcce

File tree

44 files changed

+1535
-497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1535
-497
lines changed

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Changelog
2+
3+
All notable changes to the paseto4j project will be documented in this file.
4+
5+
## 2024.3 (Unreleased)
6+
7+
### ✨ New Features
8+
9+
- 🔐 Added support for lazysodium, replacing the deprecated Tuweni library for XChaCha20Poly1305 encryption
10+
- 🧩 Introduced `Hex` class for better handling of binary data with proper equals, hashCode, and toString behavior
11+
- 🛡️ Improved key handling with typed key representations
12+
- 📦 Enhanced support for Bouncy Castle cryptographic operations
13+
14+
### 🐛 Bug Fixes
15+
16+
- 🔧 Fixed issue with SHAKE digest handling in crypto operations
17+
- 🔍 Corrected RSA key handling for proper signing operations
18+
- 🧪 Resolved issues with key length validation for secure operations
19+
- 🚫 Fixed license header issues with multi-module project structure
20+
21+
### 🔧 Technical Updates
22+
23+
- 📈 Upgraded to Bouncy Castle 1.80
24+
- 🏗️ Moved to Java 17 as minimum supported version
25+
- 🧹 Added Google Error Prone for enhanced static code analysis
26+
- 🧪 Improved test coverage with parameterized tests
27+
- 📝 Added Spotless formatting integration in the compile phase
28+
- 🔄 Updated Jackson dependencies to version 2.18.3
29+
- 🛠️ Enhanced build process with better Maven plugin configuration
30+
- 📊 Added JaCoCo for code coverage reporting
31+
32+
### 🔀 Breaking Changes
33+
34+
- ⚠️ Changed key representations to use byte arrays instead of various key types
35+
- ⚠️ Removed support for deprecated cryptographic functions
36+
- ⚠️ Refactored API to provide clearer type safety for different key types
37+
38+
## Previous Releases
39+
40+
For information about previous releases, please see the [GitHub release page](https://github.com/nbaars/paseto4j/releases).
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright © 2025 Nanne Baars
3+
* SPDX-License-Identifier: MIT
4+
*/
5+
package org.paseto4j.commons;
6+
7+
import java.util.Locale;
8+
import java.util.Objects;
9+
10+
/**
11+
* An immutable representation of binary data using a hexadecimal string for storage. This class
12+
* provides a better alternative to using byte arrays in records and ensures proper equals,
13+
* hashCode, and toString behavior.
14+
*/
15+
public record Hex(String hexValue) {
16+
17+
/** Creates a new Hex instance with validation. */
18+
public Hex {
19+
Objects.requireNonNull(hexValue, "Hex string cannot be null");
20+
HexToBytes.hexToBytes(hexValue);
21+
hexValue = hexValue.toLowerCase(Locale.ROOT);
22+
}
23+
24+
/**
25+
* Creates a new Hex instance from a byte array.
26+
*
27+
* @param bytes the byte array to convert to hexadecimal
28+
*/
29+
public Hex(byte[] bytes) {
30+
this(HexToBytes.hexEncode(Objects.requireNonNull(bytes, "Bytes cannot be null")));
31+
}
32+
33+
/**
34+
* Returns the bytes represented by this Hex object.
35+
*
36+
* @return a new byte array containing the bytes
37+
*/
38+
public byte[] toBytes() {
39+
return HexToBytes.hexToBytes(hexValue);
40+
}
41+
42+
/**
43+
* Factory method to create a Hex instance from a byte array.
44+
*
45+
* @param bytes the byte array to convert
46+
* @return a new Hex instance
47+
*/
48+
public static Hex of(byte[] bytes) {
49+
return new Hex(bytes);
50+
}
51+
52+
/**
53+
* Factory method to create a Hex instance from a hexadecimal string.
54+
*
55+
* @param hexString the hexadecimal string
56+
* @return a new Hex instance
57+
* @throws IllegalArgumentException if the string is not valid hexadecimal
58+
*/
59+
public static Hex fromString(String hexString) {
60+
return new Hex(hexString);
61+
}
62+
63+
/**
64+
* Returns the length of the byte array representation.
65+
*
66+
* @return the length in bytes
67+
*/
68+
public int length() {
69+
return hexValue.length() / 2;
70+
}
71+
}

commons/src/test/java/org/paseto4j/commons/HexToBytes.java renamed to commons/src/main/java/org/paseto4j/commons/HexToBytes.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
package org.paseto4j.commons;
66

77
/**
8-
* Copied from
9-
* <a href="http://www.docjar.com/html/api/com/sun/xml/internal/bind/DatatypeConverterImpl.java.html">...</a>
8+
* Copied from <a
9+
* href="http://www.docjar.com/html/api/com/sun/xml/internal/bind/DatatypeConverterImpl.java.html">...</a>
1010
*/
1111
public class HexToBytes {
1212

13+
private HexToBytes() {}
14+
1315
public static byte[] hexToBytes(String s) {
1416
final int len = s.length();
1517

commons/src/main/java/org/paseto4j/commons/Key.java

Lines changed: 0 additions & 51 deletions
This file was deleted.

commons/src/main/java/org/paseto4j/commons/PrivateKey.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

commons/src/main/java/org/paseto4j/commons/PublicKey.java

Lines changed: 0 additions & 20 deletions
This file was deleted.

commons/src/main/java/org/paseto4j/commons/SecretKey.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,50 @@
44
*/
55
package org.paseto4j.commons;
66

7-
public class SecretKey extends Key<javax.crypto.SecretKey> {
8-
public SecretKey(byte[] keyMaterial, Version version) {
9-
super(keyMaterial, version, 32);
7+
/**
8+
* An immutable representation of a secret key used in PASETO operations. Uses Hex for internal
9+
* storage to ensure proper equals, hashCode, and toString behavior.
10+
*/
11+
public record SecretKey(Hex key) {
12+
/**
13+
* Creates a new SecretKey with the given Hex key.
14+
*
15+
* @param key the key material as a Hex object
16+
* @throws PasetoException if the key is null or not 32 bytes in length
17+
*/
18+
public SecretKey {
19+
Conditions.verify(key != null, "Key must not be null");
20+
Conditions.verify(key.length() == 32, "Key must be 32 bytes in length");
21+
}
22+
23+
/**
24+
* Creates a new SecretKey with the given byte array.
25+
*
26+
* @param keyBytes the key material as a byte array
27+
* @return a new SecretKey instance
28+
* @throws PasetoException if the key is null or not 32 bytes in length
29+
*/
30+
public static SecretKey fromBytes(byte[] keyBytes) {
31+
Conditions.verify(keyBytes != null, "Key must not be null");
32+
Conditions.verify(keyBytes.length == 32, "Key must be a byte array of length 32");
33+
return new SecretKey(new Hex(keyBytes));
34+
}
35+
36+
public static SecretKey fromHexString(String key) {
37+
return new SecretKey(Hex.fromString(key));
38+
}
39+
40+
/**
41+
* Returns the key material as a byte array.
42+
*
43+
* @return a new byte array containing the key material
44+
*/
45+
public byte[] toBytes() {
46+
return key.toBytes();
1047
}
1148

12-
public boolean isValidFor(Version v, Purpose p) {
13-
return v == this.getVersion() && p == Purpose.PURPOSE_LOCAL;
49+
@Override
50+
public String toString() {
51+
return "****";
1452
}
1553
}

commons/src/main/java/org/paseto4j/commons/TokenAlgorithm.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,9 @@
77
import java.util.Locale;
88

99
/** Wrapper class for the chosen Paseto version */
10-
public class TokenAlgorithm {
10+
public record TokenAlgorithm(Version version, Purpose purpose) {
1111

12-
private final Version version;
13-
private final Purpose purpose;
14-
15-
public TokenAlgorithm(Version version, Purpose purpose) {
16-
this.version = version;
17-
this.purpose = purpose;
18-
}
19-
20-
/**
12+
/**
2113
* Return the header which is a concatenation of the version and the purpose
2214
*
2315
* @return the header in the format: {version}.{purpose}.

0 commit comments

Comments
 (0)