1
1
/*
2
- * Copyright 2012-2023 the original author or authors.
2
+ * Copyright 2012-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
27
27
import java .security .spec .InvalidKeySpecException ;
28
28
import java .security .spec .PKCS8EncodedKeySpec ;
29
29
import java .util .ArrayList ;
30
+ import java .util .Arrays ;
30
31
import java .util .Base64 ;
31
32
import java .util .Collections ;
33
+ import java .util .HashMap ;
34
+ import java .util .HexFormat ;
32
35
import java .util .List ;
36
+ import java .util .Map ;
33
37
import java .util .function .BiFunction ;
34
38
import java .util .regex .Matcher ;
35
39
import java .util .regex .Pattern ;
@@ -73,6 +77,26 @@ final class PemPrivateKeyParser {
73
77
74
78
public static final int BASE64_TEXT_GROUP = 1 ;
75
79
80
+ private static final EncodedOid RSA_ALGORITHM = EncodedOid .OID_1_2_840_113549_1_1_1 ;
81
+
82
+ private static final EncodedOid ELLIPTIC_CURVE_ALGORITHM = EncodedOid .OID_1_2_840_10045_2_1 ;
83
+
84
+ private static final EncodedOid ELLIPTIC_CURVE_384_BIT = EncodedOid .OID_1_3_132_0_34 ;
85
+
86
+ private static final Map <EncodedOid , String > ALGORITHMS ;
87
+ static {
88
+ Map <EncodedOid , String > algorithms = new HashMap <>();
89
+ algorithms .put (EncodedOid .OID_1_2_840_113549_1_1_1 , "RSA" );
90
+ algorithms .put (EncodedOid .OID_1_2_840_113549_1_1_10 , "RSA" );
91
+ algorithms .put (EncodedOid .OID_1_2_840_10040_4_1 , "DSA" );
92
+ algorithms .put (EncodedOid .OID_1_3_101_110 , "XDH" );
93
+ algorithms .put (EncodedOid .OID_1_3_101_111 , "XDH" );
94
+ algorithms .put (EncodedOid .OID_1_3_101_112 , "EdDSA" );
95
+ algorithms .put (EncodedOid .OID_1_3_101_113 , "EdDSA" );
96
+ algorithms .put (EncodedOid .OID_1_2_840_10045_2_1 , "EC" );
97
+ ALGORITHMS = Collections .unmodifiableMap (algorithms );
98
+ }
99
+
76
100
private static final List <PemParser > PEM_PARSERS ;
77
101
static {
78
102
List <PemParser > parsers = new ArrayList <>();
@@ -86,21 +110,6 @@ final class PemPrivateKeyParser {
86
110
PEM_PARSERS = Collections .unmodifiableList (parsers );
87
111
}
88
112
89
- /**
90
- * ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.1}.
91
- */
92
- private static final int [] RSA_ALGORITHM = { 0x2A , 0x86 , 0x48 , 0x86 , 0xF7 , 0x0D , 0x01 , 0x01 , 0x01 };
93
-
94
- /**
95
- * ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}.
96
- */
97
- private static final int [] EC_ALGORITHM = { 0x2a , 0x86 , 0x48 , 0xce , 0x3d , 0x02 , 0x01 };
98
-
99
- /**
100
- * ASN.1 encoded object identifier {@literal 1.3.132.0.34}.
101
- */
102
- private static final int [] EC_PARAMETERS = { 0x2b , 0x81 , 0x04 , 0x00 , 0x22 };
103
-
104
113
private PemPrivateKeyParser () {
105
114
}
106
115
@@ -121,29 +130,22 @@ private static PKCS8EncodedKeySpec createKeySpecForSec1Ec(byte[] bytes, String p
121
130
Assert .state (privateKey != null && privateKey .isType (ValueType .PRIMITIVE , TagType .OCTET_STRING ),
122
131
"Key spec should contain private key" );
123
132
DerElement parameters = DerElement .of (ecPrivateKey .getContents ());
124
- return createKeySpecForAlgorithm (bytes , EC_ALGORITHM , getEcParameters (parameters ));
133
+ return createKeySpecForAlgorithm (bytes , ELLIPTIC_CURVE_ALGORITHM , getEcParameters (parameters ));
125
134
}
126
135
127
- private static int [] getEcParameters (DerElement parameters ) {
136
+ private static EncodedOid getEcParameters (DerElement parameters ) {
128
137
if (parameters == null ) {
129
- return EC_PARAMETERS ;
138
+ return ELLIPTIC_CURVE_384_BIT ;
130
139
}
131
140
Assert .state (parameters .isType (ValueType .ENCODED ), "Key spec should contain encoded parameters" );
132
141
DerElement contents = DerElement .of (parameters .getContents ());
133
142
Assert .state (contents .isType (ValueType .PRIMITIVE , TagType .OBJECT_IDENTIFIER ),
134
143
"Key spec parameters should contain object identifier" );
135
- return getEcParameters ( contents . getContents () );
144
+ return EncodedOid . of ( contents );
136
145
}
137
146
138
- private static int [] getEcParameters (ByteBuffer bytes ) {
139
- int [] result = new int [bytes .remaining ()];
140
- for (int i = 0 ; i < result .length ; i ++) {
141
- result [i ] = bytes .get () & 0xFF ;
142
- }
143
- return result ;
144
- }
145
-
146
- private static PKCS8EncodedKeySpec createKeySpecForAlgorithm (byte [] bytes , int [] algorithm , int [] parameters ) {
147
+ private static PKCS8EncodedKeySpec createKeySpecForAlgorithm (byte [] bytes , EncodedOid algorithm ,
148
+ EncodedOid parameters ) {
147
149
try {
148
150
DerEncoder encoder = new DerEncoder ();
149
151
encoder .integer (0x00 ); // Version 0
@@ -160,7 +162,20 @@ private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[]
160
162
}
161
163
162
164
private static PKCS8EncodedKeySpec createKeySpecForPkcs8 (byte [] bytes , String password ) {
163
- return new PKCS8EncodedKeySpec (bytes );
165
+ DerElement ecPrivateKey = DerElement .of (bytes );
166
+ Assert .state (ecPrivateKey .isType (ValueType .ENCODED , TagType .SEQUENCE ),
167
+ "Key spec should be an ASN.1 encoded sequence" );
168
+ DerElement version = DerElement .of (ecPrivateKey .getContents ());
169
+ Assert .state (version != null && version .isType (ValueType .PRIMITIVE , TagType .INTEGER ),
170
+ "Key spec should start with version" );
171
+ DerElement sequence = DerElement .of (ecPrivateKey .getContents ());
172
+ Assert .state (sequence != null && sequence .isType (ValueType .ENCODED , TagType .SEQUENCE ),
173
+ "Key spec should contain private key" );
174
+ DerElement algorithmId = DerElement .of (sequence .getContents ());
175
+ Assert .state (algorithmId != null && algorithmId .isType (ValueType .PRIMITIVE , TagType .OBJECT_IDENTIFIER ),
176
+ "Key spec container object identifier" );
177
+ String algorithmName = ALGORITHMS .get (EncodedOid .of (algorithmId ));
178
+ return (algorithmName != null ) ? new PKCS8EncodedKeySpec (bytes , algorithmName ) : new PKCS8EncodedKeySpec (bytes );
164
179
}
165
180
166
181
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted (byte [] bytes , String password ) {
@@ -231,6 +246,15 @@ private static byte[] decodeBase64(String content) {
231
246
232
247
private PrivateKey parse (byte [] bytes , String password ) {
233
248
PKCS8EncodedKeySpec keySpec = this .keySpecFactory .apply (bytes , password );
249
+ if (keySpec .getAlgorithm () != null ) {
250
+ try {
251
+ KeyFactory keyFactory = KeyFactory .getInstance (keySpec .getAlgorithm ());
252
+ return keyFactory .generatePrivate (keySpec );
253
+ }
254
+ catch (InvalidKeySpecException | NoSuchAlgorithmException ex ) {
255
+ // Ignore
256
+ }
257
+ }
234
258
for (String algorithm : this .algorithms ) {
235
259
try {
236
260
KeyFactory keyFactory = KeyFactory .getInstance (algorithm );
@@ -251,9 +275,9 @@ static class DerEncoder {
251
275
252
276
private final ByteArrayOutputStream stream = new ByteArrayOutputStream ();
253
277
254
- void objectIdentifier (int ... encodedObjectIdentifier ) throws IOException {
255
- int code = (encodedObjectIdentifier != null ) ? 0x06 : 0x05 ;
256
- codeLengthBytes (code , bytes ( encodedObjectIdentifier ) );
278
+ void objectIdentifier (EncodedOid encodedOid ) throws IOException {
279
+ int code = (encodedOid != null ) ? 0x06 : 0x05 ;
280
+ codeLengthBytes (code , ( encodedOid != null ) ? encodedOid . toByteArray () : null );
257
281
}
258
282
259
283
void integer (int ... encodedInteger ) throws IOException {
@@ -449,4 +473,69 @@ private static String getEncryptionAlgorithm(AlgorithmParameters algParameters,
449
473
450
474
}
451
475
476
+ /**
477
+ * ANS.1 encoded object identifier.
478
+ */
479
+ static final class EncodedOid {
480
+
481
+ static final EncodedOid OID_1_2_840_10040_4_1 = EncodedOid .of ("2a8648ce380401" );
482
+ static final EncodedOid OID_1_2_840_113549_1_1_1 = EncodedOid .of ("2A864886F70D010101" );
483
+ static final EncodedOid OID_1_2_840_113549_1_1_10 = EncodedOid .of ("2a864886f70d01010a" );
484
+ static final EncodedOid OID_1_3_101_110 = EncodedOid .of ("2b656e" );
485
+ static final EncodedOid OID_1_3_101_111 = EncodedOid .of ("2b656f" );
486
+ static final EncodedOid OID_1_3_101_112 = EncodedOid .of ("2b6570" );
487
+ static final EncodedOid OID_1_3_101_113 = EncodedOid .of ("2b6571" );
488
+ static final EncodedOid OID_1_2_840_10045_2_1 = EncodedOid .of ("2a8648ce3d0201" );
489
+ static final EncodedOid OID_1_3_132_0_34 = EncodedOid .of ("2b81040022" );
490
+
491
+ private final byte [] value ;
492
+
493
+ private EncodedOid (byte [] value ) {
494
+ this .value = value ;
495
+ }
496
+
497
+ byte [] toByteArray () {
498
+ return this .value .clone ();
499
+ }
500
+
501
+ @ Override
502
+ public boolean equals (Object obj ) {
503
+ if (this == obj ) {
504
+ return true ;
505
+ }
506
+ if (obj == null || getClass () != obj .getClass ()) {
507
+ return false ;
508
+ }
509
+ return Arrays .equals (this .value , ((EncodedOid ) obj ).value );
510
+ }
511
+
512
+ @ Override
513
+ public int hashCode () {
514
+ return Arrays .hashCode (this .value );
515
+ }
516
+
517
+ static EncodedOid of (String hexString ) {
518
+ return of (HexFormat .of ().parseHex (hexString ));
519
+ }
520
+
521
+ static EncodedOid of (DerElement derElement ) {
522
+ return of (derElement .getContents ());
523
+ }
524
+
525
+ static EncodedOid of (ByteBuffer byteBuffer ) {
526
+ return of (byteBuffer .array (), byteBuffer .arrayOffset () + byteBuffer .position (), byteBuffer .remaining ());
527
+ }
528
+
529
+ static EncodedOid of (byte [] bytes ) {
530
+ return of (bytes , 0 , bytes .length );
531
+ }
532
+
533
+ static EncodedOid of (byte [] bytes , int off , int len ) {
534
+ byte [] value = new byte [len ];
535
+ System .arraycopy (bytes , off , value , 0 , len );
536
+ return new EncodedOid (value );
537
+ }
538
+
539
+ }
540
+
452
541
}
0 commit comments