From 998923e47ecd6e249f78e87e5089ad3745a688f3 Mon Sep 17 00:00:00 2001 From: siujamo Date: Thu, 24 Apr 2025 14:54:32 +0800 Subject: [PATCH] feat: load RSA key pair from pem-formatted key text --- .../onixbyte/security/impl/RsaKeyLoader.java | 93 +++++++++++++++++++ .../onixbyte/security/KeyPairLoaderTest.java | 45 --------- .../test/resources/ec_private_key_pkcs8.pem | 5 - .../src/test/resources/rsa_private_key.pem | 28 ++++++ .../src/test/resources/rsa_public_key.pem | 9 ++ 5 files changed, 130 insertions(+), 50 deletions(-) create mode 100644 key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java delete mode 100644 key-pair-loader/src/test/java/com/onixbyte/security/KeyPairLoaderTest.java delete mode 100644 key-pair-loader/src/test/resources/ec_private_key_pkcs8.pem create mode 100644 key-pair-loader/src/test/resources/rsa_private_key.pem create mode 100644 key-pair-loader/src/test/resources/rsa_public_key.pem diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java new file mode 100644 index 0000000..6cea341 --- /dev/null +++ b/key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.onixbyte.security.impl; + +import com.onixbyte.security.KeyLoader; +import com.onixbyte.security.exception.KeyLoadingException; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class RsaKeyLoader implements KeyLoader { + + private final Base64.Decoder decoder; + private final KeyFactory keyFactory; + + public RsaKeyLoader() { + try { + this.decoder = Base64.getDecoder(); + this.keyFactory = KeyFactory.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + throw new KeyLoadingException(e); + } + } + + @Override + public RSAPrivateKey loadPrivateKey(String pemKeyText) { + // Extract the raw key content + var rawKeyContent = getRawContent(pemKeyText); + + // Decode the Base64-encoded content + var keyBytes = decoder.decode(rawKeyContent); + + // Create a PKCS8EncodedKeySpec from the decoded bytes + var keySpec = new PKCS8EncodedKeySpec(keyBytes); + + try { + // Get an RSA KeyFactory and generate the private key + var _key = keyFactory.generatePrivate(keySpec); + if (_key instanceof RSAPrivateKey key) { + return key; + } else { + throw new KeyLoadingException("Unable to load private key from pem-formatted key text."); + } + } catch (InvalidKeySpecException e) { + throw new KeyLoadingException("Key spec is invalid.", e); + } + } + + @Override + public RSAPublicKey loadPublicKey(String pemKeyText) { + // Extract the raw key content + var rawKeyContent = getRawContent(pemKeyText); + + // Decode the Base64-encoded content + var keyBytes = decoder.decode(rawKeyContent); + + // Create an X509EncodedKeySpec from the decoded bytes + var keySpec = new X509EncodedKeySpec(keyBytes); + + // Get an RSA KeyFactory and generate the public key + try { + var _key = keyFactory.generatePublic(keySpec); + if (_key instanceof RSAPublicKey key) { + return key; + } else { + throw new KeyLoadingException("Unable to load public key from pem-formatted key text."); + } + } catch (InvalidKeySpecException e) { + throw new KeyLoadingException("Key spec is invalid.", e); + } + } +} diff --git a/key-pair-loader/src/test/java/com/onixbyte/security/KeyPairLoaderTest.java b/key-pair-loader/src/test/java/com/onixbyte/security/KeyPairLoaderTest.java deleted file mode 100644 index ca57f74..0000000 --- a/key-pair-loader/src/test/java/com/onixbyte/security/KeyPairLoaderTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024-2024 OnixByte. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.onixbyte.security; - -import com.onixbyte.security.impl.EcKeyLoader; -import org.junit.jupiter.api.Test; - -public class KeyPairLoaderTest { - - @Test - public void test() { - var keyLoader = new EcKeyLoader(); - // The following key pair is only used for test only, and is already exposed to public. - // DO NOT USE THEM FOR PRODUCTION! - var privateKey = keyLoader.loadPrivateKey(""" - -----BEGIN PRIVATE KEY----- - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs79JlARgXEf6EDV7 - +PHQCTHEMtqIoHOy1GZ1+ynQJ6yhRANCAARkA7GRY2i4gg8qx0XViAXUP9cPw9pn - Jg1wfrQ41FaMyqVBejNYxvaLtamErF/ySimnjafMJ+VZCh34lBj6Ez8R - -----END PRIVATE KEY----- - """); - var publicKey = keyLoader.loadPublicKey(""" - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZAOxkWNouIIPKsdF1YgF1D/XD8Pa - ZyYNcH60ONRWjMqlQXozWMb2i7WphKxf8kopp42nzCflWQod+JQY+hM/EQ== - -----END PUBLIC KEY----- - """); - } - -} diff --git a/key-pair-loader/src/test/resources/ec_private_key_pkcs8.pem b/key-pair-loader/src/test/resources/ec_private_key_pkcs8.pem deleted file mode 100644 index 02dfcc8..0000000 --- a/key-pair-loader/src/test/resources/ec_private_key_pkcs8.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs79JlARgXEf6EDV7 -+PHQCTHEMtqIoHOy1GZ1+ynQJ6yhRANCAARkA7GRY2i4gg8qx0XViAXUP9cPw9pn -Jg1wfrQ41FaMyqVBejNYxvaLtamErF/ySimnjafMJ+VZCh34lBj6Ez8R ------END PRIVATE KEY----- diff --git a/key-pair-loader/src/test/resources/rsa_private_key.pem b/key-pair-loader/src/test/resources/rsa_private_key.pem new file mode 100644 index 0000000..f8b0fc4 --- /dev/null +++ b/key-pair-loader/src/test/resources/rsa_private_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4VIFYJFMAs15j +J3V3IicHd7sI2TIFqTZME40zlOlVAlPKLZmTQvZFLNgaUAAsvPi5i1DR2ywwK6Al +BfnwVnzvmDXC5mKHOz4oxOQVA6Nlp2yVaQMzidmfYNSkMtcv/4HRPsatc7K/M5l6 +pCP20DVRjkikBdIy8e9w+x6BrIFp5Q8PZc/X2BGNAUMMYACdeYH5R/A0CxqkND13 +esc4gkynMOrvZrZGHCz51usfSCqyDWWwsN+GG6LYWia4GkNlS0erQnP8gS93dfjl +e96BIfy3z7Iv+kUrf5ikNW2P8jMxLAv6LO+dcUAu9k477wIAF7Iq5KMuH/otsDOu ++h+2qXmBAgMBAAECggEAdRqcmC0g+y6arxV3fkObthjPGYAa57KBCWUa7B0n30+m +pavVRS2Jpttb2SSqwG4ouI6rARti/iBEd9EWqTCP4AieKZetFOpqCJ24lPRPRGus +d9S6jr5N4qut+vSCp37NABijZj4uJ540nTH0R7qtuhTnynl4Q0/1wwiYvTvVF1Lg +dn+I/8aRbshwDhdAOWOUe6GL7/eaCYgN8/UmlKIpp8tg0w2iWxbaFiR7gZiM41LA +M6SXXfcCas+ZVXsGbzQ3SNiVurCGuuRNcCScXS3/WoEDIb3cNtp49iOmQS+nmEoo +wh4uiEd+0+BrzxngS4o5+mKnHJnwgY0+veGVYLMR5QKBgQD9WKQmevMDU5c+NPq9 +8jaR457Fuxq1gwzeFNJdWfOc/K2LEWh+nFNFCb++EboEj6FdxWaWNMxbrmJps5gs +EoBUYy/Tl7UycDqDfiYLmDdTsf2pVjjh9jaIADiLcJ8S6wwJMZKub7Tp8UVkenAl +535MqShLUC11Y7VxLb3Tsll4XwKBgQD67mm6iCmshr/eszPfNE3ylZ+PiNa7nat7 +N7lQzBIiRJflT1kmVidC5gE+jASqH728ChkZZKxbHsjxpmWdAhLOITdXoTB4sDsd +wtV1lxkXxK9FnrpFvO3y1wZ/QsD3Z2KXxHYZqawkUETO9F3nqAXW0b2GDar5Qiyo +J3Tx/43aHwKBgDC0NMJtCoDONhowZy/S+6iqQKC0qprQec3L5PErVMkOTnKYwyTr ++pogGKt6ju9HiXcUdvdTaSIK8UJu00dNuzv94XjlBmGO78DNpJTAC4rcge5m9AKE +qdEVcclkukARzbuKuy8rrHT4/CUn4J141m/4aRWpcUPLCluato6XD9ozAoGBANvf +JhOFFgcPd3YazfvpZ9eE1XA+tfFlYYmxNRcgCU+vjO0oDvSxjutmgHae18N91pG6 +w21lskSRf/+GDwl5dKLbphOJsOA/gz07qDDGOf2CoRW+1Hcg6drcINxH0K+4DkLv +qZApBSY4k2JH6zR+HMeztn6M4WBRZLHfCPC3PUN/AoGAA3AoHbLTZvqMIKSDkP4Y +U/tTsSFDY4aYo7LG/jk8af3oPU3KyGh4ZFBd6aMmXbS8f8FjvmrM+/e+y9OOGAlq +iOl0hYrs5cJSMLW6i4KnJYuYbMkgmk3bN2t9apu64xKR94gbPrI6AGnPZp+iIzp0 +hXKe4HcuhQ3G0a2hjayiQ84= +-----END PRIVATE KEY----- diff --git a/key-pair-loader/src/test/resources/rsa_public_key.pem b/key-pair-loader/src/test/resources/rsa_public_key.pem new file mode 100644 index 0000000..edde855 --- /dev/null +++ b/key-pair-loader/src/test/resources/rsa_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+FSBWCRTALNeYyd1dyIn +B3e7CNkyBak2TBONM5TpVQJTyi2Zk0L2RSzYGlAALLz4uYtQ0dssMCugJQX58FZ8 +75g1wuZihzs+KMTkFQOjZadslWkDM4nZn2DUpDLXL/+B0T7GrXOyvzOZeqQj9tA1 +UY5IpAXSMvHvcPsegayBaeUPD2XP19gRjQFDDGAAnXmB+UfwNAsapDQ9d3rHOIJM +pzDq72a2Rhws+dbrH0gqsg1lsLDfhhui2FomuBpDZUtHq0Jz/IEvd3X45XvegSH8 +t8+yL/pFK3+YpDVtj/IzMSwL+izvnXFALvZOO+8CABeyKuSjLh/6LbAzrvoftql5 +gQIDAQAB +-----END PUBLIC KEY-----