From 8e7cb5fab8ba04ce5e8ce0b8a8fa57c777e5d667 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Wed, 26 Oct 2022 14:04:39 +0200 Subject: [PATCH] Use only public APIs to read PKCS#1 keys (#5240) Co-authored-by: Weijun Wang Signed-off-by: Tomas Langer --- common/key-util/pom.xml | 48 ++----------- .../java/io/helidon/common/pki/DerUtils.java | 61 ---------------- .../java/io/helidon/common/pki/PemReader.java | 9 +-- .../java/io/helidon/common/pki/Pkcs1Util.java | 69 +++++++++++++++++++ .../io/helidon/common/pki/KeyConfigTest.java | 15 +++- .../test/resources/keystore/pkcs1-1024.pem | 15 ++++ .../test/resources/keystore/pkcs1-2048.pem | 27 ++++++++ .../src/test/resources/keystore/pkcs1-512.pem | 9 +++ pom.xml | 3 - 9 files changed, 141 insertions(+), 115 deletions(-) delete mode 100644 common/key-util/src/main/java/io/helidon/common/pki/DerUtils.java create mode 100644 common/key-util/src/main/java/io/helidon/common/pki/Pkcs1Util.java create mode 100644 common/key-util/src/test/resources/keystore/pkcs1-1024.pem create mode 100644 common/key-util/src/test/resources/keystore/pkcs1-2048.pem create mode 100644 common/key-util/src/test/resources/keystore/pkcs1-512.pem diff --git a/common/key-util/pom.xml b/common/key-util/pom.xml index 9abe779f0bd..816ce3fb372 100644 --- a/common/key-util/pom.xml +++ b/common/key-util/pom.xml @@ -56,6 +56,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-params + test + org.hamcrest hamcrest-core @@ -68,47 +73,4 @@ - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - - --add-exports=java.base/sun.security.util=io.helidon.common.pki - - - - - - - - - javadoc - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - --add-exports=java.base/sun.security.util=io.helidon.common.pki - - - - - - - jar - - - - - - - - diff --git a/common/key-util/src/main/java/io/helidon/common/pki/DerUtils.java b/common/key-util/src/main/java/io/helidon/common/pki/DerUtils.java deleted file mode 100644 index 63bd14fd567..00000000000 --- a/common/key-util/src/main/java/io/helidon/common/pki/DerUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 Oracle and/or its affiliates. - * - * 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 io.helidon.common.pki; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.spec.KeySpec; -import java.security.spec.RSAPrivateCrtKeySpec; - -final class DerUtils { - private DerUtils() { - } - - static void checkEnabled() { - Module javaBase = String.class.getModule(); - Module myModule = DerUtils.class.getModule(); - - if (!javaBase.isExported("sun.security.util", myModule)) { - //--add-exports java.base/sun.security.util=io.helidon.common.pki - throw new PkiException("Cannot read PKCS#1 key specification, as package sun.security.util " - + "is not exported to this module. Please add --add-exports " - + "java.base/sun.security.util=io.helidon.common.pki to java " - + "command line options"); - } - } - - static KeySpec pkcs1RsaKeySpec(byte[] bytes) { - try { - // fully qualified class names allow us to compile this without failure - sun.security.util.DerInputStream derReader = new sun.security.util.DerInputStream(bytes); - sun.security.util.DerValue[] seq = derReader.getSequence(0); - // skip version seq[0]; - BigInteger modulus = seq[1].getBigInteger(); - BigInteger publicExp = seq[2].getBigInteger(); - BigInteger privateExp = seq[3].getBigInteger(); - BigInteger prime1 = seq[4].getBigInteger(); - BigInteger prime2 = seq[5].getBigInteger(); - BigInteger exp1 = seq[6].getBigInteger(); - BigInteger exp2 = seq[7].getBigInteger(); - BigInteger crtCoef = seq[8].getBigInteger(); - - return new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef); - } catch (IOException e) { - throw new PkiException("Failed to get PKCS#1 RSA key spec", e); - } - } -} diff --git a/common/key-util/src/main/java/io/helidon/common/pki/PemReader.java b/common/key-util/src/main/java/io/helidon/common/pki/PemReader.java index f3941170e15..13fb1e545f7 100644 --- a/common/key-util/src/main/java/io/helidon/common/pki/PemReader.java +++ b/common/key-util/src/main/java/io/helidon/common/pki/PemReader.java @@ -96,7 +96,7 @@ static PrivateKey readPrivateKey(InputStream input, char[] password) { switch (pkInfo.type) { case "PKCS1-RSA": - return rsaPrivateKey(pkcs1RsaKeySpec(pkInfo.bytes)); + return rsaPrivateKey(Pkcs1Util.pkcs1RsaKeySpec(pkInfo.bytes)); case "PKCS1-DSA": throw new UnsupportedOperationException("PKCS#1 DSA private key is not supported"); case "PKCS1-EC": @@ -107,11 +107,6 @@ static PrivateKey readPrivateKey(InputStream input, char[] password) { } } - private static KeySpec pkcs1RsaKeySpec(byte[] bytes) { - DerUtils.checkEnabled(); - return DerUtils.pkcs1RsaKeySpec(bytes); - } - private static PrivateKey pkcs8(KeySpec keySpec) { try { return rsaPrivateKey(keySpec); @@ -223,7 +218,7 @@ private static X509EncodedKeySpec generatePublicKeySpec(byte[] bytes) { return new X509EncodedKeySpec(bytes); } - private static PrivateKeyInfo readPrivateKeyBytes(InputStream in) { + static PrivateKeyInfo readPrivateKeyBytes(InputStream in) { String content; try { content = readContent(in); diff --git a/common/key-util/src/main/java/io/helidon/common/pki/Pkcs1Util.java b/common/key-util/src/main/java/io/helidon/common/pki/Pkcs1Util.java new file mode 100644 index 00000000000..f0740c11f07 --- /dev/null +++ b/common/key-util/src/main/java/io/helidon/common/pki/Pkcs1Util.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. + * + * 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 io.helidon.common.pki; + +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.HexFormat; + +final class Pkcs1Util { + // this is a constant for RSA (and we only support RSA) + private static final byte[] RSA_ALG = HexFormat.of().parseHex("020100300D06092A864886F70D0101010500"); + + private Pkcs1Util() { + } + + static KeySpec pkcs1RsaKeySpec(byte[] bytes) { + return new PKCS8EncodedKeySpec(pkcs1ToPkcs8(bytes)); + } + + // Code provided by Weijun Wang + private static byte[] pkcs1ToPkcs8(byte[] pkcs1Bytes) { + + // PKCS #8 key will look like + // 30 len1 + // 02 01 00 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 + // 04 len2 + // p1 + + byte[] len2 = encodeLen(pkcs1Bytes.length); + int p8len = pkcs1Bytes.length + len2.length + 1 + RSA_ALG.length; + byte[] len1 = encodeLen(p8len); + byte[] pkcs8bytes = new byte[1 + len1.length + p8len]; + + pkcs8bytes[0] = 0x30; + System.arraycopy(len1, 0, pkcs8bytes, 1, len1.length); + System.arraycopy(RSA_ALG, 0, pkcs8bytes, 1 + len1.length, RSA_ALG.length); + pkcs8bytes[1 + len1.length + RSA_ALG.length] = 0x04; + System.arraycopy(len2, 0, pkcs8bytes, 1 + len1.length + RSA_ALG.length + 1, len2.length); + System.arraycopy(pkcs1Bytes, 0, pkcs8bytes, 1 + len1.length + RSA_ALG.length + 1 + len2.length, pkcs1Bytes.length); + + return pkcs8bytes; + } + + private static byte[] encodeLen(int len) { + if (len < 128) { + return new byte[] {(byte) len}; + } else if (len < (1 << 8)) { + return new byte[] {(byte) 0x081, (byte) len}; + } else if (len < (1 << 16)) { + return new byte[] {(byte) 0x082, (byte) (len >> 8), (byte) len}; + } else { + throw new PkiException("PKCS#1 key of unexpected size: " + len); + } + } +} diff --git a/common/key-util/src/test/java/io/helidon/common/pki/KeyConfigTest.java b/common/key-util/src/test/java/io/helidon/common/pki/KeyConfigTest.java index 1690dec973e..2daed586d6a 100644 --- a/common/key-util/src/test/java/io/helidon/common/pki/KeyConfigTest.java +++ b/common/key-util/src/test/java/io/helidon/common/pki/KeyConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. + * Copyright (c) 2017, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package io.helidon.common.pki; +import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -27,10 +28,13 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -55,6 +59,15 @@ void testConfigPublicKey() { assertThat(publicKey.publicKey().isPresent(), is(true)); } + @ParameterizedTest + @CsvSource({"512", "1024","2048"}) + void testPkcs1(String length) { + PrivateKey privateKey = PemReader.readPrivateKey(KeyConfigTest.class.getResourceAsStream("/keystore/pkcs1-" + length + + ".pem"), null); + assertThat(privateKey, notNullValue()); + assertThat(privateKey.getAlgorithm(), is("RSA")); + } + @Test void testOldPublicKey() { KeyConfig publicKey = KeyConfig.create(config.get("unit-2")); diff --git a/common/key-util/src/test/resources/keystore/pkcs1-1024.pem b/common/key-util/src/test/resources/keystore/pkcs1-1024.pem new file mode 100644 index 00000000000..de8a9f56bdb --- /dev/null +++ b/common/key-util/src/test/resources/keystore/pkcs1-1024.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCvkRPTPbnOGP3r+AJieU4HKocaDAGP3AyASrCfhAC3DJB5RL7Y +3yhUqzwILbvE6wcbngZZkHka7Fzh7ZMtNdTsMI8UfsXtVtn0hNN1a/s9yt5J38xR +LJAedA2XJQNJ5Cay4YkJbntegtO07sPu8JPBoiODn1/GMWez3URp6JJW1wIDAQAB +AoGAbsL2YPS2PkIiIDatOncRNRAtf89HRP0snduBJoHe+ZzhoMAwLx5KkXAeRYKk +zY0BRPkjRGoTHVs1FgwOKB2oH/aTyuEJ3nupFgvW2eLH3MQAL8yLc2YCufogBRMT +3L1OskTk6XeaHLK2RWv1IilkY6WKERZf0/2FtipSC+adTcECQQDd3HjV/rX4yyHR +wJ/zIZ4lzko1uzT4dSndDf9dWD/fzq6w8hnsyxJDTVS5m00FqVIhjzoKMrvJUJGC +FPmPmMjRAkEAypT6aUJ1NmjDyMakIHwCN3oM70ghKQJTOuqL1e2J4TQYU9zAGaJm +5FQJb7px9C/8cw73BUBkvAOLto91Je+PJwJAXTeVTdSHgNFYlFjq26z0Vc4nQAw4 +ZWxU+pw2/3Fk5RRiMdaHLgbk1YJYZuPpqMdLyu3y5PYMELnZaV6GvN7lAQJALkVS +4OHuFcxeE6jTahwJAZTeCXVnJY/DZOyXnfhQiuC0QctlETXX3IUZVqy2RHkFZ15e +q5Nmrs78hWlE77JE9wJBAJC2MaObm6grGEuWfe8NoBg8QC6sI0i6lOVpXUDvYV2e +mixgnByUBbgu5MG0OEP95OpVxQTd7OubX9qwVemTRhU= +-----END RSA PRIVATE KEY----- diff --git a/common/key-util/src/test/resources/keystore/pkcs1-2048.pem b/common/key-util/src/test/resources/keystore/pkcs1-2048.pem new file mode 100644 index 00000000000..4a37244ba17 --- /dev/null +++ b/common/key-util/src/test/resources/keystore/pkcs1-2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArznMqI0bdj8vXaEKoLhrG/NT8eo5aIlRPQnM3819iSjgrS+0 +/3iRZg0LghTgUSm6czeFzeuKzDvOlV83n+NZUPWWZH3LlMmxJMN523IAJpT54ORC +wjyo6bSoTxHEjYTWO5yMtdJfR+uozd8aAX876R7Shq9YlBKoNKyzFSEvgPVEuA8V +sSpekeksEMw2HR4Izh2GSBCto2WOXe9lJFWMtjRBGbOiD62Z0qpJ6i+lCEr9wnwl +ToT4f3P0hfpmmjZX0X8N4h+dAm/N1WRTeQw6VSbTztNtYNaJdX6p3T2PViagd9xF +gUAf7cmyx7SnAc3GOiRNko6Yt5INekOvkMyPrwIDAQABAoIBAANgesg4x/G0cAY+ +50SHqVDFlLWRzP9tvgoOGUuq2yN8jS/pPnS57xtnXvRn0Jbf1f8Ib+SzCF69PFL2 +nczQBdWglgBjyDua03Yg6kVHYidcMaCa5Yp2vs6aM7AqaH5NtA88Ch4q6rkpGny1 +MvwaZp4sgOQllvBrl6BEP8NFe3PhvgJDCABfQXvnDA7q2HiK9MahBcNkQaOHwXgK +L9Ssvn6UF9Z0LSvt+0SWvqckPSbncTJN1FIGGnHv+jHZVeMjSe6/aX/rjwjp1qGu +gGbB3vNLUUj71LlF1rCsPj3Na2NA4TZxXS3lNGjHiP19UHav7RDa7f9ELftIEaKP +ndElMDkCgYEA1q1kYXu0h5g0G/Cal+Kz3YKNDHsFQ9sRmfqQ6Isk6QEeEVR4hrZi +lJBAXDRRKcLIwVMAATSHRF/1liF2mYfTITg/IrDLhTNE2adHPWw+yx6PuI/uQpOd +jxiuVK024rl6DzSg7casORrwhnN6qgS7zf5men/KH33pbRABp+3LCyUCgYEA0PRc +0RyuA7z+pewfGCuIGlpm2CvmhGDgA9WN8WCFvF7Szvm5qZ35f5Hd0cMQ5HCPy8pJ +I4XwouLNmDHv461pR54LXxCu6Hx+wgKCAooCzTqW/S+rdZLvnGmY0AJ9NeaaOw2a +rz08esrAfc5jWM7HBg+OOMjzpojmqtPPOsNvgUMCgYEAxWUYKP7bh9avC4XYUJK2 ++pZBZdl0hOlZrPEV742KOem6IQs/6/amfJ6LX30HqFOfzwuntHP9cSSfKBXK/O9E +doZGn3pbGTaEN3I18kenEZQfaQCHf5ZGST7Tha7kCeOsVXD6DMkisTuRML/caZsC +qS4kQr1gOEbJrWwLacMgcTUCgYA5i+L4ED74dpdnGMVjgbGlGFqUlFqTAJ8RT0Id +ROjv/Olv6SSxyvkIoiKF/4PqdfmUNWy5JM0l/vKCRNZ9TKfe+m7FSrHxA0BhrBEk +I+Arp0QoDHXbFpF48TgNqXHUY2L8en2sX2AFrUsgGrQPpDr5t1UC3I0Fw1RLnbPH +ykUuQwKBgQDVIkquG3SybEHEdruwf7qOa3yFNO3G+8A7DrsyMJWltlaoeAfp+aUY +jXPBisQEyPaXT/wWuk9WqPotw0UKEDxcmXbkbsZvcc2G+CnUpOsdhTJAX28ggUJN +d1ix5Xm4Px0Djcsoq7C7NYwvAepQfK6RfucFdE6XvJpZ/dUY1cBs8w== +-----END RSA PRIVATE KEY----- diff --git a/common/key-util/src/test/resources/keystore/pkcs1-512.pem b/common/key-util/src/test/resources/keystore/pkcs1-512.pem new file mode 100644 index 00000000000..7e45218de4a --- /dev/null +++ b/common/key-util/src/test/resources/keystore/pkcs1-512.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAMqJuM6TvqUEb0UlQDSQF2aXgD4JH7hojJ01m/1f3q91lnOpMzab ++kBre1u3vtoGnl2+FqDKr/tthys8vkenyTMCAwEAAQJBAK78mkzwXTBZSoFlE7nW +HEGo90WhwGQlAAf7f1BD+jOAS4XM5bBui4m77Qr0e3GaRd7EeYnW5yAZoKy+nrmb +dwECIQD4nxUsEf4B6DWwdzctWKdx0iCxrIomR9Oe1aHI+5tKQQIhANCMhGxJGiiq +f6N0YNrUeRCZfk4cjVuiizXKQ8hYne5zAiEAlKjyzPY5LsS9jbXLHWc8QDfH6tVj +ib47EGdnJLklwsECIDXHRo61+yzpaqi35hIIIIALVOrHqhwrOkLQudH8KB3JAiEA +w4vdFCrQlE3C+RfkK/TVn+r7kZ/hIqb99rTsbI6cnZ8= +-----END RSA PRIVATE KEY----- diff --git a/pom.xml b/pom.xml index 5d6f225e590..fc77c420981 100644 --- a/pom.xml +++ b/pom.xml @@ -383,9 +383,6 @@ -J-Dhttp.agent=maven-javadoc-plugin - --add-exports=java.base/sun.security.util=io.helidon.common.pki - -