Skip to content

Commit 5f9ea34

Browse files
author
zihluwang
committed
feat: extracted ECDSA key pair loader
1 parent e28559a commit 5f9ea34

File tree

3 files changed

+137
-77
lines changed

3 files changed

+137
-77
lines changed

key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -18,105 +18,38 @@
1818
package com.onixbyte.security;
1919

2020
import com.onixbyte.security.exception.KeyLoadingException;
21-
import org.slf4j.Logger;
22-
import org.slf4j.LoggerFactory;
2321

24-
import java.security.KeyFactory;
25-
import java.security.NoSuchAlgorithmException;
22+
import java.security.PrivateKey;
23+
import java.security.PublicKey;
2624
import java.security.interfaces.ECPrivateKey;
27-
import java.security.interfaces.ECPublicKey;
28-
import java.security.spec.InvalidKeySpecException;
29-
import java.security.spec.PKCS8EncodedKeySpec;
30-
import java.security.spec.X509EncodedKeySpec;
31-
import java.util.Base64;
3225

3326
/**
34-
* The {@code KeyLoader} class provides utility methods for loading ECDSA keys from PEM-formatted
27+
* The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted
3528
* key text. This class supports loading both private and public keys.
3629
* <p>
3730
* The utility methods in this class are useful for scenarios where ECDSA keys need to be loaded
3831
* from PEM-formatted strings for cryptographic operations.
39-
* </p>
40-
*
41-
* <p><b>Example usage:</b></p>
42-
* <pre>{@code
43-
* String pemPrivateKey = """
44-
* -----BEGIN PRIVATE KEY-----
45-
* ...
46-
* -----END PRIVATE KEY-----""";
47-
* ECPrivateKey privateKey = KeyLoader.loadEcdsaPrivateKey(pemPrivateKey);
48-
*
49-
* String pemPublicKey = """
50-
* -----BEGIN PUBLIC KEY-----
51-
* ...
52-
* -----END PUBLIC KEY-----""";
53-
* ECPublicKey publicKey = KeyLoader.loadEcdsaPublicKey(pemPublicKey);
54-
* }</pre>
5532
*
5633
* @author zihluwang
5734
* @version 1.6.0
5835
* @since 1.6.0
5936
*/
60-
public class KeyLoader {
61-
62-
private final static Logger log = LoggerFactory.getLogger(KeyLoader.class);
63-
64-
/**
65-
* Private constructor prevents from being initialised.
66-
*/
67-
private KeyLoader() {
68-
}
37+
public interface KeyLoader {
6938

7039
/**
71-
* Load ECDSA private key from pem-formatted key text.
40+
* Load private key from pem-formatted key text.
7241
*
7342
* @param pemKeyText pem-formatted key text
7443
* @return loaded private key
75-
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
76-
* or EC Key Factory is not loaded, or key spec is invalid
7744
*/
78-
public static ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) {
79-
try {
80-
var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
81-
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
82-
var keyFactory = KeyFactory.getInstance("EC");
83-
var _key = keyFactory.generatePrivate(keySpec);
84-
if (_key instanceof ECPrivateKey privateKey) {
85-
return privateKey;
86-
} else {
87-
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
88-
}
89-
} catch (NoSuchAlgorithmException e) {
90-
throw new KeyLoadingException("Cannot get EC Key Factory.", e);
91-
} catch (InvalidKeySpecException e) {
92-
throw new KeyLoadingException("Key spec is invalid.", e);
93-
}
94-
}
45+
PrivateKey loadPrivateKey(String pemKeyText);
9546

9647
/**
97-
* Load ECDSA public key from pem-formatted key text.
48+
* Load public key from pem-formatted key text.
9849
*
9950
* @param pemKeyText pem-formatted key text
10051
* @return loaded private key
101-
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
102-
* or EC Key Factory is not loaded, or key spec is invalid
10352
*/
104-
public static ECPublicKey loadEcdsaPublicKey(String pemKeyText) {
105-
try {
106-
var keyBytes = Base64.getDecoder().decode(pemKeyText);
107-
var spec = new X509EncodedKeySpec(keyBytes);
108-
var keyFactory = KeyFactory.getInstance("EC");
109-
var key = keyFactory.generatePublic(spec);
110-
if (key instanceof ECPublicKey publicKey) {
111-
return publicKey;
112-
} else {
113-
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
114-
}
115-
} catch (NoSuchAlgorithmException e) {
116-
throw new KeyLoadingException("Cannot get EC Key Factory.", e);
117-
} catch (InvalidKeySpecException e) {
118-
throw new KeyLoadingException("Key spec is invalid.", e);
119-
}
120-
}
53+
PublicKey loadPublicKey(String pemKeyText);
12154

12255
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (C) 2024-2025 OnixByte.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.onixbyte.security.impl;
19+
20+
import com.onixbyte.security.KeyLoader;
21+
import com.onixbyte.security.exception.KeyLoadingException;
22+
23+
import java.security.KeyFactory;
24+
import java.security.NoSuchAlgorithmException;
25+
import java.security.interfaces.ECPrivateKey;
26+
import java.security.interfaces.ECPublicKey;
27+
import java.security.spec.InvalidKeySpecException;
28+
import java.security.spec.PKCS8EncodedKeySpec;
29+
import java.security.spec.X509EncodedKeySpec;
30+
import java.util.Base64;
31+
32+
/**
33+
* Key pair loader for loading key pairs for ECDSA-based algorithms.
34+
* <p>
35+
*
36+
* <b>Example usage for ECDSA:</b>
37+
* <pre>{@code
38+
* KeyLoader keyLoader = new EcKeyLoader();
39+
* String pemPrivateKey = """
40+
* -----BEGIN EC PRIVATE KEY-----
41+
* ...
42+
* -----END EC PRIVATE KEY-----""";
43+
* ECPrivateKey privateKey = KeyLoader.loadEcdsaPrivateKey(pemPrivateKey);
44+
*
45+
* String pemPublicKey = """
46+
* -----BEGIN EC PUBLIC KEY-----
47+
* ...
48+
* -----END EC PUBLIC KEY-----""";
49+
* ECPublicKey publicKey = KeyLoader.loadPublicKey(pemPublicKey);
50+
* }</pre>
51+
*/
52+
public class EcKeyLoader implements KeyLoader {
53+
54+
private final KeyFactory keyFactory;
55+
56+
/**
57+
* Initialise a key loader for EC-based algorithms.
58+
*
59+
* @throws NoSuchAlgorithmException if no {@code Provider} supports a {@code KeyFactorySpi}
60+
* implementation for the specified algorithm
61+
*/
62+
public EcKeyLoader() throws NoSuchAlgorithmException {
63+
this.keyFactory = KeyFactory.getInstance("EC");
64+
}
65+
66+
/**
67+
* Load ECDSA private key from pem-formatted key text.
68+
*
69+
* @param pemKeyText pem-formatted key text
70+
* @return loaded private key
71+
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
72+
* or EC Key Factory is not loaded, or key spec is invalid
73+
*/
74+
@Override
75+
public ECPrivateKey loadPrivateKey(String pemKeyText) {
76+
try {
77+
// remove all unnecessary parts of the pem key text
78+
pemKeyText = pemKeyText
79+
.replaceAll("-----BEGIN EC PRIVATE KEY-----", "")
80+
.replaceAll("-----END EC PRIVATE KEY-----", "")
81+
.replaceAll("\n", "");
82+
var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
83+
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
84+
85+
var _key = keyFactory.generatePrivate(keySpec);
86+
if (_key instanceof ECPrivateKey privateKey) {
87+
return privateKey;
88+
} else {
89+
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
90+
}
91+
} catch (InvalidKeySpecException e) {
92+
throw new KeyLoadingException("Key spec is invalid.", e);
93+
}
94+
}
95+
96+
/**
97+
* Load public key from pem-formatted key text.
98+
*
99+
* @param pemKeyText pem-formatted key text
100+
* @return loaded private key
101+
* @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
102+
* or EC Key Factory is not loaded, or key spec is invalid
103+
*/
104+
@Override
105+
public ECPublicKey loadPublicKey(String pemKeyText) {
106+
try {
107+
// remove all unnecessary parts of the pem key text
108+
pemKeyText = pemKeyText
109+
.replaceAll("-----BEGIN EC PUBLIC KEY-----", "")
110+
.replaceAll("-----END EC PUBLIC KEY-----", "")
111+
.replaceAll("\n", "");
112+
var keyBytes = Base64.getDecoder().decode(pemKeyText);
113+
var spec = new X509EncodedKeySpec(keyBytes);
114+
var key = keyFactory.generatePublic(spec);
115+
if (key instanceof ECPublicKey publicKey) {
116+
return publicKey;
117+
} else {
118+
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
119+
}
120+
} catch (InvalidKeySpecException e) {
121+
throw new KeyLoadingException("Key spec is invalid.", e);
122+
}
123+
}
124+
125+
}

simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.onixbyte.devkit.utils.Base64Util;
2121
import com.onixbyte.guid.GuidCreator;
2222
import com.onixbyte.security.KeyLoader;
23+
import com.onixbyte.security.impl.EcKeyLoader;
2324
import com.onixbyte.simplejwt.TokenPayload;
2425
import com.onixbyte.simplejwt.TokenResolver;
2526
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
@@ -178,8 +179,9 @@ public Builder secret(String secret) {
178179
* @return the builder instance
179180
*/
180181
public Builder keyPair(String publicKey, String privateKey) {
181-
this.publicKey = KeyLoader.loadEcdsaPublicKey(publicKey);
182-
this.privateKey = KeyLoader.loadEcdsaPrivateKey(privateKey);
182+
var keyLoader = new EcKeyLoader();
183+
this.publicKey = keyLoader.loadPublicKey(publicKey);
184+
this.privateKey = keyLoader.loadPrivateKey(privateKey);
183185
return this;
184186
}
185187

0 commit comments

Comments
 (0)