13
13
import com .nimbusds .jose .crypto .RSASSASigner ;
14
14
import com .nimbusds .jwt .JWTClaimsSet ;
15
15
import com .nimbusds .jwt .SignedJWT ;
16
+ import java .io .FileReader ;
16
17
import java .io .IOException ;
17
18
import java .io .StringReader ;
18
19
import java .nio .file .Files ;
37
38
import net .snowflake .client .log .SFLogger ;
38
39
import net .snowflake .client .log .SFLoggerFactory ;
39
40
import org .apache .commons .codec .binary .Base64 ;
41
+ import org .bouncycastle .asn1 .pkcs .PrivateKeyInfo ;
42
+ import org .bouncycastle .openssl .PEMKeyPair ;
43
+ import org .bouncycastle .openssl .PEMParser ;
44
+ import org .bouncycastle .openssl .jcajce .JcaPEMKeyConverter ;
45
+ import org .bouncycastle .openssl .jcajce .JceOpenSSLPKCS8DecryptorProviderBuilder ;
46
+ import org .bouncycastle .operator .InputDecryptorProvider ;
47
+ import org .bouncycastle .operator .OperatorCreationException ;
48
+ import org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo ;
49
+ import org .bouncycastle .pkcs .PKCSException ;
40
50
import org .bouncycastle .util .io .pem .PemReader ;
41
51
42
52
/** Class used to compute jwt token for key pair authentication Created by hyu on 1/16/18. */
@@ -58,14 +68,14 @@ class SessionUtilKeyPair {
58
68
59
69
private Provider SecurityProvider = null ;
60
70
61
- private SecretKeyFactory secretKeyFactory = null ;
62
-
63
71
private static final String ISSUER_FMT = "%s.%s.%s" ;
64
72
65
73
private static final String SUBJECT_FMT = "%s.%s" ;
66
74
67
75
private static final int JWT_DEFAULT_AUTH_TIMEOUT = 10 ;
68
76
77
+ private boolean isBouncyCastleProviderEnabled = false ;
78
+
69
79
SessionUtilKeyPair (
70
80
PrivateKey privateKey ,
71
81
String privateKeyFile ,
@@ -75,10 +85,14 @@ class SessionUtilKeyPair {
75
85
throws SFException {
76
86
this .userName = userName .toUpperCase ();
77
87
this .accountName = accountName .toUpperCase ();
78
-
88
+ String enableBouncyCastleJvm =
89
+ System .getProperty (SecurityUtil .ENABLE_BOUNCYCASTLE_PROVIDER_JVM );
90
+ if (enableBouncyCastleJvm != null ) {
91
+ isBouncyCastleProviderEnabled = enableBouncyCastleJvm .equalsIgnoreCase ("true" );
92
+ }
79
93
// check if in FIPS mode
80
94
for (Provider p : Security .getProviders ()) {
81
- if ("BCFIPS" .equals (p .getName ())) {
95
+ if (SecurityUtil . BOUNCY_CASTLE_FIPS_PROVIDER .equals (p .getName ())) {
82
96
this .isFipsMode = true ;
83
97
this .SecurityProvider = p ;
84
98
break ;
@@ -133,37 +147,31 @@ private SecretKeyFactory getSecretKeyFactory(String algorithm) throws NoSuchAlgo
133
147
134
148
private PrivateKey extractPrivateKeyFromFile (String privateKeyFile , String privateKeyFilePwd )
135
149
throws SFException {
136
- try {
137
- String privateKeyContent = new String (Files .readAllBytes (Paths .get (privateKeyFile )));
138
- if (Strings .isNullOrEmpty (privateKeyFilePwd )) {
139
- // unencrypted private key file
140
- PemReader pr = new PemReader (new StringReader (privateKeyContent ));
141
- byte [] decoded = pr .readPemObject ().getContent ();
142
- pr .close ();
143
- PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec (decoded );
144
- KeyFactory keyFactory = getKeyFactoryInstance ();
145
- return keyFactory .generatePrivate (encodedKeySpec );
146
- } else {
147
- // encrypted private key file
148
- PemReader pr = new PemReader (new StringReader (privateKeyContent ));
149
- byte [] decoded = pr .readPemObject ().getContent ();
150
- pr .close ();
151
- EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo (decoded );
152
- PBEKeySpec keySpec = new PBEKeySpec (privateKeyFilePwd .toCharArray ());
153
- SecretKeyFactory pbeKeyFactory = this .getSecretKeyFactory (pkInfo .getAlgName ());
154
- PKCS8EncodedKeySpec encodedKeySpec =
155
- pkInfo .getKeySpec (pbeKeyFactory .generateSecret (keySpec ));
156
- KeyFactory keyFactory = getKeyFactoryInstance ();
157
- return keyFactory .generatePrivate (encodedKeySpec );
150
+
151
+ if (isBouncyCastleProviderEnabled ) {
152
+ try {
153
+ return extractPrivateKeyWithBouncyCastle (privateKeyFile , privateKeyFilePwd );
154
+ } catch (IOException | PKCSException | OperatorCreationException e ) {
155
+ logger .error ("Could not extract private key using Bouncy Castle provider" , e );
156
+ throw new SFException (e , ErrorCode .INVALID_OR_UNSUPPORTED_PRIVATE_KEY , e .getCause ());
157
+ }
158
+ } else {
159
+ try {
160
+ return extractPrivateKeyWithJdk (privateKeyFile , privateKeyFilePwd );
161
+ } catch (NoSuchAlgorithmException
162
+ | InvalidKeySpecException
163
+ | IOException
164
+ | IllegalArgumentException
165
+ | NullPointerException
166
+ | InvalidKeyException e ) {
167
+ logger .error (
168
+ "Could not extract private key. Try setting the JVM argument: " + "-D{}" + "=TRUE" ,
169
+ SecurityUtil .ENABLE_BOUNCYCASTLE_PROVIDER_JVM );
170
+ throw new SFException (
171
+ e ,
172
+ ErrorCode .INVALID_OR_UNSUPPORTED_PRIVATE_KEY ,
173
+ privateKeyFile + ": " + e .getMessage ());
158
174
}
159
- } catch (NoSuchAlgorithmException
160
- | InvalidKeySpecException
161
- | IOException
162
- | IllegalArgumentException
163
- | NullPointerException
164
- | InvalidKeyException e ) {
165
- throw new SFException (
166
- e , ErrorCode .INVALID_OR_UNSUPPORTED_PRIVATE_KEY , privateKeyFile + ": " + e .getMessage ());
167
175
}
168
176
}
169
177
@@ -222,4 +230,72 @@ public static int getTimeout() {
222
230
}
223
231
return jwtAuthTimeout ;
224
232
}
233
+
234
+ private PrivateKey extractPrivateKeyWithBouncyCastle (
235
+ String privateKeyFile , String privateKeyFilePwd )
236
+ throws IOException , PKCSException , OperatorCreationException {
237
+ PrivateKeyInfo privateKeyInfo = null ;
238
+ PEMParser pemParser = new PEMParser (new FileReader (Paths .get (privateKeyFile ).toFile ()));
239
+ Object pemObject = pemParser .readObject ();
240
+ if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo ) {
241
+ // Handle the case where the private key is encrypted.
242
+ PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo =
243
+ (PKCS8EncryptedPrivateKeyInfo ) pemObject ;
244
+ InputDecryptorProvider pkcs8Prov =
245
+ new JceOpenSSLPKCS8DecryptorProviderBuilder ().build (privateKeyFilePwd .toCharArray ());
246
+ privateKeyInfo = encryptedPrivateKeyInfo .decryptPrivateKeyInfo (pkcs8Prov );
247
+ } else if (pemObject instanceof PEMKeyPair ) {
248
+ // PKCS#1 private key
249
+ privateKeyInfo = ((PEMKeyPair ) pemObject ).getPrivateKeyInfo ();
250
+ } else if (pemObject instanceof PrivateKeyInfo ) {
251
+ // Handle the case where the private key is unencrypted.
252
+ privateKeyInfo = (PrivateKeyInfo ) pemObject ;
253
+ }
254
+ pemParser .close ();
255
+ JcaPEMKeyConverter converter =
256
+ new JcaPEMKeyConverter ()
257
+ .setProvider (
258
+ isFipsMode
259
+ ? SecurityUtil .BOUNCY_CASTLE_FIPS_PROVIDER
260
+ : SecurityUtil .BOUNCY_CASTLE_PROVIDER );
261
+ return converter .getPrivateKey (privateKeyInfo );
262
+ }
263
+
264
+ private PrivateKey extractPrivateKeyWithJdk (String privateKeyFile , String privateKeyFilePwd )
265
+ throws IOException , NoSuchAlgorithmException , InvalidKeySpecException , InvalidKeyException {
266
+ String privateKeyContent = new String (Files .readAllBytes (Paths .get (privateKeyFile )));
267
+ if (Strings .isNullOrEmpty (privateKeyFilePwd )) {
268
+ // unencrypted private key file
269
+ return generatePrivateKey (false , privateKeyContent , privateKeyFilePwd );
270
+ } else {
271
+ // encrypted private key file
272
+ return generatePrivateKey (true , privateKeyContent , privateKeyFilePwd );
273
+ }
274
+ }
275
+
276
+ private PrivateKey generatePrivateKey (
277
+ boolean isEncrypted , String privateKeyContent , String privateKeyFilePwd )
278
+ throws IOException , NoSuchAlgorithmException , InvalidKeySpecException , InvalidKeyException {
279
+ if (isEncrypted ) {
280
+ try (PemReader pr = new PemReader (new StringReader (privateKeyContent ))) {
281
+ byte [] decoded = pr .readPemObject ().getContent ();
282
+ pr .close ();
283
+ EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo (decoded );
284
+ PBEKeySpec keySpec = new PBEKeySpec (privateKeyFilePwd .toCharArray ());
285
+ SecretKeyFactory pbeKeyFactory = this .getSecretKeyFactory (pkInfo .getAlgName ());
286
+ PKCS8EncodedKeySpec encodedKeySpec =
287
+ pkInfo .getKeySpec (pbeKeyFactory .generateSecret (keySpec ));
288
+ KeyFactory keyFactory = getKeyFactoryInstance ();
289
+ return keyFactory .generatePrivate (encodedKeySpec );
290
+ }
291
+ } else {
292
+ try (PemReader pr = new PemReader (new StringReader (privateKeyContent ))) {
293
+ byte [] decoded = pr .readPemObject ().getContent ();
294
+ pr .close ();
295
+ PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec (decoded );
296
+ KeyFactory keyFactory = getKeyFactoryInstance ();
297
+ return keyFactory .generatePrivate (encodedKeySpec );
298
+ }
299
+ }
300
+ }
225
301
}
0 commit comments