77 * Licensed under the Apache License, Version 2.0 (the "License");
88 * you may not use this file except in compliance with the License.
99 * You may obtain a copy of the License at
10- *
10+ *
1111 * http://www.apache.org/licenses/LICENSE-2.0
12- *
12+ *
1313 * Unless required by applicable law or agreed to in writing, software
1414 * distributed under the License is distributed on an "AS IS" BASIS,
1515 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3232 */
3333final class PKCS1PEMKey {
3434
35+ // Matches RSA PEM format
3536 private static final Pattern PKCS1_PEM_KEY_PATTERN =
36- Pattern .compile ("(?m)(?s)^---*BEGIN RSA PRIVATE KEY.*---*$(.*)^---*END.*---*$.*" );
37+ Pattern .compile ("(?m)(?s)^\\ s*-{3,}\\ s*BEGIN\\ s+RSA\\ s+PRIVATE\\ s+KEY\\ s*-{3,}\\ s*$(.*)^\\ s*-{3,}\\ s*END\\ s+RSA\\ s+PRIVATE\\ s+KEY\\ s*-{3,}\\ s*$.*" );
38+
39+ // Matches PKCS8 PEM format
40+ private static final Pattern PKCS8_PEM_KEY_PATTERN =
41+ Pattern .compile ("(?m)(?s)^\\ s*-{3,}\\ s*BEGIN\\ s+PRIVATE\\ s+KEY\\ s*-{3,}\\ s*$(.*)^\\ s*-{3,}\\ s*END\\ s+PRIVATE\\ s+KEY\\ s*-{3,}\\ s*$.*" );
3742
3843 private PKCS1PEMKey () {}
3944
4045 /**
41- * Try to interpret the supplied key as a PKCS#1 PEM file .
46+ * Try to interpret the supplied key as a PEM file ( PKCS#1 or PKCS#8) .
4247 *
4348 * @param privateKey the private key to use
4449 */
4550 public static Optional <KeySpec > loadKeySpec (final byte [] privateKey ) {
46- final Matcher isPEM = PKCS1_PEM_KEY_PATTERN .matcher (new String (privateKey ));
47- if (!isPEM .matches ()) {
48- return Optional .empty ();
51+ final String keyString = new String (privateKey );
52+
53+ // Try to match PKCS1 (RSA) format first
54+ Matcher isPKCS1 = PKCS1_PEM_KEY_PATTERN .matcher (keyString );
55+ if (isPKCS1 .matches ()) {
56+ return extractKeySpec (isPKCS1 .group (1 ), true );
57+ }
58+
59+ // Try to match PKCS8 format
60+ Matcher isPKCS8 = PKCS8_PEM_KEY_PATTERN .matcher (keyString );
61+ if (isPKCS8 .matches ()) {
62+ return extractKeySpec (isPKCS8 .group (1 ), false );
4963 }
5064
51- byte [] pkcs1Key = Base64 .getMimeDecoder ().decode (isPEM .group (1 ));
52- byte [] pkcs8Key = toPkcs8 (pkcs1Key );
53- final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec (pkcs8Key );
54- return Optional .of (keySpec );
65+ // Not a recognized PEM format
66+ return Optional .empty ();
67+ }
68+
69+ /**
70+ * Extract a KeySpec from the base64 content.
71+ *
72+ * @param base64Content the base64 content from the PEM file
73+ * @param isPKCS1 whether this is PKCS1 format (needs conversion to PKCS8)
74+ * @return an Optional containing the KeySpec if successful
75+ */
76+ private static Optional <KeySpec > extractKeySpec (String base64Content , boolean isPKCS1 ) {
77+ try {
78+ // Remove all whitespace
79+ base64Content = base64Content .replaceAll ("\\ s+" , "" );
80+
81+ // Check if content is empty after whitespace removal
82+ if (base64Content .isEmpty ()) {
83+ return Optional .empty ();
84+ }
85+
86+ // Decode the base64 content
87+ byte [] decodedKey = Base64 .getDecoder ().decode (base64Content );
88+
89+ // Convert to PKCS8 if necessary
90+ byte [] pkcs8Key = isPKCS1 ? toPkcs8 (decodedKey ) : decodedKey ;
91+
92+ return Optional .of (new PKCS8EncodedKeySpec (pkcs8Key ));
93+ } catch (IllegalArgumentException e ) {
94+ // Failed to decode base64 content
95+ return Optional .empty ();
96+ }
5597 }
5698
5799 /**
@@ -68,15 +110,15 @@ private static byte[] toPkcs8(final byte[] pkcs1Bytes) {
68110 final int pkcs1Length = pkcs1Bytes .length ;
69111 final int totalLength = pkcs1Length + 22 ;
70112 byte [] pkcs8Header = new byte [] {
71- 0x30 , (byte ) 0x82 , (byte ) ((totalLength >> 8 ) & 0xff ), (byte ) (totalLength & 0xff ), // Sequence + total length
72- 0x2 , 0x1 , 0x0 , // Integer (0)
73- 0x30 , 0xD , 0x6 , 0x9 , 0x2A , (byte ) 0x86 , 0x48 , (byte ) 0x86 , (byte ) 0xF7 , 0xD , 0x1 , 0x1 , 0x1 , 0x5 , 0x0 , // Sequence: 1.2.840.113549.1.1.1, NULL
74- 0x4 , (byte ) 0x82 , (byte ) ((pkcs1Length >> 8 ) & 0xff ), (byte ) (pkcs1Length & 0xff ) // Octet string + length
113+ 0x30 , (byte ) 0x82 , (byte ) ((totalLength >> 8 ) & 0xff ), (byte ) (totalLength & 0xff ), // Sequence + total length
114+ 0x2 , 0x1 , 0x0 , // Integer (0)
115+ 0x30 , 0xD , 0x6 , 0x9 , 0x2A , (byte ) 0x86 , 0x48 , (byte ) 0x86 , (byte ) 0xF7 , 0xD , 0x1 , 0x1 , 0x1 , 0x5 , 0x0 , // Sequence: 1.2.840.113549.1.1.1, NULL
116+ 0x4 , (byte ) 0x82 , (byte ) ((pkcs1Length >> 8 ) & 0xff ), (byte ) (pkcs1Length & 0xff ) // Octet string + length
75117 };
76118
77119 byte [] pkcs8bytes = new byte [pkcs8Header .length + pkcs1Bytes .length ];
78120 System .arraycopy (pkcs8Header , 0 , pkcs8bytes , 0 , pkcs8Header .length );
79121 System .arraycopy (pkcs1Bytes , 0 , pkcs8bytes , pkcs8Header .length , pkcs1Bytes .length );
80122 return pkcs8bytes ;
81123 }
82- }
124+ }
0 commit comments