Skip to content

Commit 144685d

Browse files
committed
Merge branch 'main' into develop
2 parents 098a2fd + 5343c14 commit 144685d

File tree

8 files changed

+258
-69
lines changed

8 files changed

+258
-69
lines changed

key-pair-loader/README.md

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,71 @@ ZyYNcH60ONRWjMqlQXozWMb2i7WphKxf8kopp42nzCflWQod+JQY+hM/EQ==
4343
-----END PUBLIC KEY-----
4444
```
4545

46-
#### Convert private key to EC formats which could be acceptable by Java
46+
## RSA-based algorithm
4747

48-
Java's `PKCS8EncodedKeySpec` requires the private key to be in PKCS#8 format, while OpenSSL by
49-
default generates private keys in traditional PEM format. To convert the private key, run the
50-
following command:
48+
### Generate key pair
49+
50+
#### Generate private key
51+
52+
Generate a private key by `genpkey` command provided by OpenSSL:
5153

5254
```shell
53-
openssl pkcs8 -topk8 -inform PEM -outform PEM -in ec_private_key.pem -out ec_private_key_pkcs8.pem -nocrypt
55+
openssl genpkey -algorithm RSA -out rsa_private_key.pem -pkeyopt rsa_keygen_bits:2048
5456
```
5557

56-
The converted private key will look like this:
58+
The output of this command is a file called `rsa_private_key.pem` and its content looks like the
59+
following:
5760

5861
```text
5962
-----BEGIN PRIVATE KEY-----
60-
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs79JlARgXEf6EDV7
61-
+PHQCTHEMtqIoHOy1GZ1+ynQJ6yhRANCAARkA7GRY2i4gg8qx0XViAXUP9cPw9pn
62-
Jg1wfrQ41FaMyqVBejNYxvaLtamErF/ySimnjafMJ+VZCh34lBj6Ez8R
63+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4VIFYJFMAs15j
64+
J3V3IicHd7sI2TIFqTZME40zlOlVAlPKLZmTQvZFLNgaUAAsvPi5i1DR2ywwK6Al
65+
BfnwVnzvmDXC5mKHOz4oxOQVA6Nlp2yVaQMzidmfYNSkMtcv/4HRPsatc7K/M5l6
66+
pCP20DVRjkikBdIy8e9w+x6BrIFp5Q8PZc/X2BGNAUMMYACdeYH5R/A0CxqkND13
67+
esc4gkynMOrvZrZGHCz51usfSCqyDWWwsN+GG6LYWia4GkNlS0erQnP8gS93dfjl
68+
e96BIfy3z7Iv+kUrf5ikNW2P8jMxLAv6LO+dcUAu9k477wIAF7Iq5KMuH/otsDOu
69+
+h+2qXmBAgMBAAECggEAdRqcmC0g+y6arxV3fkObthjPGYAa57KBCWUa7B0n30+m
70+
pavVRS2Jpttb2SSqwG4ouI6rARti/iBEd9EWqTCP4AieKZetFOpqCJ24lPRPRGus
71+
d9S6jr5N4qut+vSCp37NABijZj4uJ540nTH0R7qtuhTnynl4Q0/1wwiYvTvVF1Lg
72+
dn+I/8aRbshwDhdAOWOUe6GL7/eaCYgN8/UmlKIpp8tg0w2iWxbaFiR7gZiM41LA
73+
M6SXXfcCas+ZVXsGbzQ3SNiVurCGuuRNcCScXS3/WoEDIb3cNtp49iOmQS+nmEoo
74+
wh4uiEd+0+BrzxngS4o5+mKnHJnwgY0+veGVYLMR5QKBgQD9WKQmevMDU5c+NPq9
75+
8jaR457Fuxq1gwzeFNJdWfOc/K2LEWh+nFNFCb++EboEj6FdxWaWNMxbrmJps5gs
76+
EoBUYy/Tl7UycDqDfiYLmDdTsf2pVjjh9jaIADiLcJ8S6wwJMZKub7Tp8UVkenAl
77+
535MqShLUC11Y7VxLb3Tsll4XwKBgQD67mm6iCmshr/eszPfNE3ylZ+PiNa7nat7
78+
N7lQzBIiRJflT1kmVidC5gE+jASqH728ChkZZKxbHsjxpmWdAhLOITdXoTB4sDsd
79+
wtV1lxkXxK9FnrpFvO3y1wZ/QsD3Z2KXxHYZqawkUETO9F3nqAXW0b2GDar5Qiyo
80+
J3Tx/43aHwKBgDC0NMJtCoDONhowZy/S+6iqQKC0qprQec3L5PErVMkOTnKYwyTr
81+
+pogGKt6ju9HiXcUdvdTaSIK8UJu00dNuzv94XjlBmGO78DNpJTAC4rcge5m9AKE
82+
qdEVcclkukARzbuKuy8rrHT4/CUn4J141m/4aRWpcUPLCluato6XD9ozAoGBANvf
83+
JhOFFgcPd3YazfvpZ9eE1XA+tfFlYYmxNRcgCU+vjO0oDvSxjutmgHae18N91pG6
84+
w21lskSRf/+GDwl5dKLbphOJsOA/gz07qDDGOf2CoRW+1Hcg6drcINxH0K+4DkLv
85+
qZApBSY4k2JH6zR+HMeztn6M4WBRZLHfCPC3PUN/AoGAA3AoHbLTZvqMIKSDkP4Y
86+
U/tTsSFDY4aYo7LG/jk8af3oPU3KyGh4ZFBd6aMmXbS8f8FjvmrM+/e+y9OOGAlq
87+
iOl0hYrs5cJSMLW6i4KnJYuYbMkgmk3bN2t9apu64xKR94gbPrI6AGnPZp+iIzp0
88+
hXKe4HcuhQ3G0a2hjayiQ84=
6389
-----END PRIVATE KEY-----
90+
```
91+
92+
#### Generate public key by private key
93+
94+
Export public key from private key by OpenSSL:
95+
96+
```shell
97+
openssl pkey -in rsa_private_key.pem -pubout -out rsa_public_key.pem
98+
```
99+
100+
The output of this command is a file called `rsa_public_key.pem` and its content looks like the
101+
following:
102+
103+
```text
104+
-----BEGIN PUBLIC KEY-----
105+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+FSBWCRTALNeYyd1dyIn
106+
B3e7CNkyBak2TBONM5TpVQJTyi2Zk0L2RSzYGlAALLz4uYtQ0dssMCugJQX58FZ8
107+
75g1wuZihzs+KMTkFQOjZadslWkDM4nZn2DUpDLXL/+B0T7GrXOyvzOZeqQj9tA1
108+
UY5IpAXSMvHvcPsegayBaeUPD2XP19gRjQFDDGAAnXmB+UfwNAsapDQ9d3rHOIJM
109+
pzDq72a2Rhws+dbrH0gqsg1lsLDfhhui2FomuBpDZUtHq0Jz/IEvd3X45XvegSH8
110+
t8+yL/pFK3+YpDVtj/IzMSwL+izvnXFALvZOO+8CABeyKuSjLh/6LbAzrvoftql5
111+
gQIDAQAB
112+
-----END PUBLIC KEY-----
64113
```

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,29 @@ public interface KeyLoader {
4949
*/
5050
PublicKey loadPublicKey(String pemKeyText);
5151

52+
/**
53+
* Retrieves the raw content of a PEM formatted key by removing unnecessary headers, footers,
54+
* and new line characters.
55+
*
56+
* <p>
57+
* This method processes the provided PEM key text to return a cleaned string that contains
58+
* only the key content. The method strips away the
59+
* {@code "-----BEGIN (EC )?(PRIVATE|PUBLIC) KEY-----"} and
60+
* {@code "-----END (EC )?(PRIVATE|PUBLIC) KEY-----"} lines, as well as any new line characters,
61+
* resulting in a continuous string representation of the key, which can be used for further
62+
* cryptographic operations.
63+
*
64+
* @param pemKeyText the PEM formatted key as a string, which may include headers, footers and
65+
* line breaks
66+
* @return a string containing the raw key content devoid of any unnecessary formatting
67+
* or whitespace
68+
*/
69+
default String getRawContent(String pemKeyText) {
70+
// remove all unnecessary parts of the pem key text
71+
return pemKeyText
72+
.replaceAll("-----BEGIN (EC )?(PRIVATE|PUBLIC) KEY-----", "")
73+
.replaceAll("-----END (EC )?(PRIVATE|PUBLIC) KEY-----", "")
74+
.replaceAll("\n", "");
75+
}
76+
5277
}

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,7 @@ public EcKeyLoader() {
8282
@Override
8383
public ECPrivateKey loadPrivateKey(String pemKeyText) {
8484
try {
85-
// remove all unnecessary parts of the pem key text
86-
pemKeyText = pemKeyText
87-
.replaceAll("-----BEGIN (EC )?PRIVATE KEY-----", "")
88-
.replaceAll("-----END (EC )?PRIVATE KEY-----", "")
89-
.replaceAll("\n", "");
85+
pemKeyText = getRawContent(pemKeyText);
9086
var decodedKeyString = decoder.decode(pemKeyText);
9187
var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
9288

@@ -112,11 +108,7 @@ public ECPrivateKey loadPrivateKey(String pemKeyText) {
112108
@Override
113109
public ECPublicKey loadPublicKey(String pemKeyText) {
114110
try {
115-
// remove all unnecessary parts of the pem key text
116-
pemKeyText = pemKeyText
117-
.replaceAll("-----BEGIN (EC )?PUBLIC KEY-----", "")
118-
.replaceAll("-----END (EC )?PUBLIC KEY-----", "")
119-
.replaceAll("\n", "");
111+
pemKeyText = getRawContent(pemKeyText);
120112
var keyBytes = decoder.decode(pemKeyText);
121113
var spec = new X509EncodedKeySpec(keyBytes);
122114
var key = keyFactory.generatePublic(spec);
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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.RSAPrivateKey;
26+
import java.security.interfaces.RSAPublicKey;
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+
* A class responsible for loading RSA keys from PEM formatted text.
34+
* <p>
35+
* This class implements the {@link KeyLoader} interface and provides methods to load both private
36+
* and public RSA keys. The keys are expected to be in the standard PEM format, which includes
37+
* Base64-encoded key content surrounded by header and footer lines. The class handles the decoding
38+
* of Base64 content and the generation of keys using the RSA key factory.
39+
* <p>
40+
* Any exceptions encountered during the loading process are encapsulated in a
41+
* {@link KeyLoadingException}, allowing for flexible error handling.
42+
*
43+
* @author siujamo
44+
* @see KeyLoader
45+
* @see KeyLoadingException
46+
*/
47+
public class RsaKeyLoader implements KeyLoader {
48+
49+
private final Base64.Decoder decoder;
50+
private final KeyFactory keyFactory;
51+
52+
/**
53+
* Constructs an instance of {@code RsaKeyLoader}.
54+
* <p>
55+
* This constructor initialises the Base64 decoder and the RSA {@link KeyFactory}. It may throw
56+
* a {@link KeyLoadingException} if the RSA algorithm is not available.
57+
*/
58+
public RsaKeyLoader() {
59+
try {
60+
this.decoder = Base64.getDecoder();
61+
this.keyFactory = KeyFactory.getInstance("RSA");
62+
} catch (NoSuchAlgorithmException e) {
63+
throw new KeyLoadingException(e);
64+
}
65+
}
66+
67+
/**
68+
* Loads an RSA private key from a given PEM formatted key text.
69+
* <p>
70+
* This method extracts the raw key content from the provided PEM text, decodes the
71+
* Base64-encoded content, and generates an instance of {@link RSAPrivateKey}. If the key cannot
72+
* be loaded due to invalid specifications or types, a {@link KeyLoadingException} is thrown.
73+
*
74+
* @param pemKeyText the PEM formatted private key text
75+
* @return an instance of {@link RSAPrivateKey}
76+
* @throws KeyLoadingException if the key loading process encounters an error
77+
*/
78+
@Override
79+
public RSAPrivateKey loadPrivateKey(String pemKeyText) {
80+
// Extract the raw key content
81+
var rawKeyContent = getRawContent(pemKeyText);
82+
83+
// Decode the Base64-encoded content
84+
var keyBytes = decoder.decode(rawKeyContent);
85+
86+
// Create a PKCS8EncodedKeySpec from the decoded bytes
87+
var keySpec = new PKCS8EncodedKeySpec(keyBytes);
88+
89+
try {
90+
// Get an RSA KeyFactory and generate the private key
91+
var _key = keyFactory.generatePrivate(keySpec);
92+
if (_key instanceof RSAPrivateKey key) {
93+
return key;
94+
} else {
95+
throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
96+
}
97+
} catch (InvalidKeySpecException e) {
98+
throw new KeyLoadingException("Key spec is invalid.", e);
99+
}
100+
}
101+
102+
/**
103+
* Loads an RSA public key from a given PEM formatted key text.
104+
* <p>
105+
* This method extracts the raw key content from the provided PEM text, decodes the
106+
* Base64-encoded content, and generates an instance of {@link RSAPublicKey}. If the key cannot
107+
* be loaded due to invalid specifications or types, a {@link KeyLoadingException} is thrown.
108+
*
109+
* @param pemKeyText the PEM formatted public key text
110+
* @return an instance of {@link RSAPublicKey}
111+
* @throws KeyLoadingException if the key loading process encounters an error
112+
*/
113+
@Override
114+
public RSAPublicKey loadPublicKey(String pemKeyText) {
115+
// Extract the raw key content
116+
var rawKeyContent = getRawContent(pemKeyText);
117+
118+
// Decode the Base64-encoded content
119+
var keyBytes = decoder.decode(rawKeyContent);
120+
121+
// Create an X509EncodedKeySpec from the decoded bytes
122+
var keySpec = new X509EncodedKeySpec(keyBytes);
123+
124+
// Get an RSA KeyFactory and generate the public key
125+
try {
126+
var _key = keyFactory.generatePublic(keySpec);
127+
if (_key instanceof RSAPublicKey key) {
128+
return key;
129+
} else {
130+
throw new KeyLoadingException("Unable to load public key from pem-formatted key text.");
131+
}
132+
} catch (InvalidKeySpecException e) {
133+
throw new KeyLoadingException("Key spec is invalid.", e);
134+
}
135+
}
136+
}

key-pair-loader/src/test/java/com/onixbyte/security/KeyPairLoaderTest.java

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

key-pair-loader/src/test/resources/ec_private_key_pkcs8.pem

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4VIFYJFMAs15j
3+
J3V3IicHd7sI2TIFqTZME40zlOlVAlPKLZmTQvZFLNgaUAAsvPi5i1DR2ywwK6Al
4+
BfnwVnzvmDXC5mKHOz4oxOQVA6Nlp2yVaQMzidmfYNSkMtcv/4HRPsatc7K/M5l6
5+
pCP20DVRjkikBdIy8e9w+x6BrIFp5Q8PZc/X2BGNAUMMYACdeYH5R/A0CxqkND13
6+
esc4gkynMOrvZrZGHCz51usfSCqyDWWwsN+GG6LYWia4GkNlS0erQnP8gS93dfjl
7+
e96BIfy3z7Iv+kUrf5ikNW2P8jMxLAv6LO+dcUAu9k477wIAF7Iq5KMuH/otsDOu
8+
+h+2qXmBAgMBAAECggEAdRqcmC0g+y6arxV3fkObthjPGYAa57KBCWUa7B0n30+m
9+
pavVRS2Jpttb2SSqwG4ouI6rARti/iBEd9EWqTCP4AieKZetFOpqCJ24lPRPRGus
10+
d9S6jr5N4qut+vSCp37NABijZj4uJ540nTH0R7qtuhTnynl4Q0/1wwiYvTvVF1Lg
11+
dn+I/8aRbshwDhdAOWOUe6GL7/eaCYgN8/UmlKIpp8tg0w2iWxbaFiR7gZiM41LA
12+
M6SXXfcCas+ZVXsGbzQ3SNiVurCGuuRNcCScXS3/WoEDIb3cNtp49iOmQS+nmEoo
13+
wh4uiEd+0+BrzxngS4o5+mKnHJnwgY0+veGVYLMR5QKBgQD9WKQmevMDU5c+NPq9
14+
8jaR457Fuxq1gwzeFNJdWfOc/K2LEWh+nFNFCb++EboEj6FdxWaWNMxbrmJps5gs
15+
EoBUYy/Tl7UycDqDfiYLmDdTsf2pVjjh9jaIADiLcJ8S6wwJMZKub7Tp8UVkenAl
16+
535MqShLUC11Y7VxLb3Tsll4XwKBgQD67mm6iCmshr/eszPfNE3ylZ+PiNa7nat7
17+
N7lQzBIiRJflT1kmVidC5gE+jASqH728ChkZZKxbHsjxpmWdAhLOITdXoTB4sDsd
18+
wtV1lxkXxK9FnrpFvO3y1wZ/QsD3Z2KXxHYZqawkUETO9F3nqAXW0b2GDar5Qiyo
19+
J3Tx/43aHwKBgDC0NMJtCoDONhowZy/S+6iqQKC0qprQec3L5PErVMkOTnKYwyTr
20+
+pogGKt6ju9HiXcUdvdTaSIK8UJu00dNuzv94XjlBmGO78DNpJTAC4rcge5m9AKE
21+
qdEVcclkukARzbuKuy8rrHT4/CUn4J141m/4aRWpcUPLCluato6XD9ozAoGBANvf
22+
JhOFFgcPd3YazfvpZ9eE1XA+tfFlYYmxNRcgCU+vjO0oDvSxjutmgHae18N91pG6
23+
w21lskSRf/+GDwl5dKLbphOJsOA/gz07qDDGOf2CoRW+1Hcg6drcINxH0K+4DkLv
24+
qZApBSY4k2JH6zR+HMeztn6M4WBRZLHfCPC3PUN/AoGAA3AoHbLTZvqMIKSDkP4Y
25+
U/tTsSFDY4aYo7LG/jk8af3oPU3KyGh4ZFBd6aMmXbS8f8FjvmrM+/e+y9OOGAlq
26+
iOl0hYrs5cJSMLW6i4KnJYuYbMkgmk3bN2t9apu64xKR94gbPrI6AGnPZp+iIzp0
27+
hXKe4HcuhQ3G0a2hjayiQ84=
28+
-----END PRIVATE KEY-----
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+FSBWCRTALNeYyd1dyIn
3+
B3e7CNkyBak2TBONM5TpVQJTyi2Zk0L2RSzYGlAALLz4uYtQ0dssMCugJQX58FZ8
4+
75g1wuZihzs+KMTkFQOjZadslWkDM4nZn2DUpDLXL/+B0T7GrXOyvzOZeqQj9tA1
5+
UY5IpAXSMvHvcPsegayBaeUPD2XP19gRjQFDDGAAnXmB+UfwNAsapDQ9d3rHOIJM
6+
pzDq72a2Rhws+dbrH0gqsg1lsLDfhhui2FomuBpDZUtHq0Jz/IEvd3X45XvegSH8
7+
t8+yL/pFK3+YpDVtj/IzMSwL+izvnXFALvZOO+8CABeyKuSjLh/6LbAzrvoftql5
8+
gQIDAQAB
9+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)