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 .PEMParser ;
43
+ import org .bouncycastle .openssl .jcajce .JcaPEMKeyConverter ;
44
+ import org .bouncycastle .openssl .jcajce .JceOpenSSLPKCS8DecryptorProviderBuilder ;
45
+ import org .bouncycastle .operator .InputDecryptorProvider ;
46
+ import org .bouncycastle .operator .OperatorCreationException ;
47
+ import org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo ;
48
+ import org .bouncycastle .pkcs .PKCSException ;
40
49
import org .bouncycastle .util .io .pem .PemReader ;
41
50
42
51
/** Class used to compute jwt token for key pair authentication Created by hyu on 1/16/18. */
@@ -66,6 +75,14 @@ class SessionUtilKeyPair {
66
75
67
76
private static final int JWT_DEFAULT_AUTH_TIMEOUT = 10 ;
68
77
78
+ /** provider name */
79
+ private static final String BOUNCY_CASTLE_PROVIDER = "BC" ;
80
+
81
+ /** provider name for FIPS */
82
+ private static final String BOUNCY_CASTLE_FIPS_PROVIDER = "BCFIPS" ;
83
+
84
+ private boolean ENABLE_BOUNCYCASTLE_PROVIDER = true ;
85
+
69
86
SessionUtilKeyPair (
70
87
PrivateKey privateKey ,
71
88
String privateKeyFile ,
@@ -78,7 +95,7 @@ class SessionUtilKeyPair {
78
95
79
96
// check if in FIPS mode
80
97
for (Provider p : Security .getProviders ()) {
81
- if ("BCFIPS" .equals (p .getName ())) {
98
+ if (BOUNCY_CASTLE_FIPS_PROVIDER .equals (p .getName ())) {
82
99
this .isFipsMode = true ;
83
100
this .SecurityProvider = p ;
84
101
break ;
@@ -133,37 +150,30 @@ private SecretKeyFactory getSecretKeyFactory(String algorithm) throws NoSuchAlgo
133
150
134
151
private PrivateKey extractPrivateKeyFromFile (String privateKeyFile , String privateKeyFilePwd )
135
152
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 );
153
+
154
+ if (ENABLE_BOUNCYCASTLE_PROVIDER ) {
155
+ try {
156
+ return extractPrivateKeyWithBouncyCastle (privateKeyFile , privateKeyFilePwd );
157
+ } catch (IOException | PKCSException | OperatorCreationException e ) {
158
+ logger .error ("Could not extract private key using Bouncy Castle provider" );
159
+ throw new SFException (e , ErrorCode .INVALID_OR_UNSUPPORTED_PRIVATE_KEY , e .getCause ());
160
+ }
161
+ } else {
162
+ try {
163
+ return extractPrivateKeyWithJdk (privateKeyFile , privateKeyFilePwd );
164
+ } catch (NoSuchAlgorithmException
165
+ | InvalidKeySpecException
166
+ | IOException
167
+ | IllegalArgumentException
168
+ | NullPointerException
169
+ | InvalidKeyException e ) {
170
+ logger .error (
171
+ "Could not extract private key. Try setting " + ENABLE_BOUNCYCASTLE_PROVIDER + "=TRUE" );
172
+ throw new SFException (
173
+ e ,
174
+ ErrorCode .INVALID_OR_UNSUPPORTED_PRIVATE_KEY ,
175
+ privateKeyFile + ": " + e .getMessage ());
158
176
}
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
177
}
168
178
}
169
179
@@ -222,4 +232,51 @@ public static int getTimeout() {
222
232
}
223
233
return jwtAuthTimeout ;
224
234
}
235
+
236
+ private PrivateKey extractPrivateKeyWithBouncyCastle (
237
+ String privateKeyFile , String privateKeyFilePwd )
238
+ throws IOException , PKCSException , OperatorCreationException {
239
+ PrivateKeyInfo privateKeyInfo = null ;
240
+ PEMParser pemParser = new PEMParser (new FileReader (Paths .get (privateKeyFile ).toFile ()));
241
+ Object pemObject = pemParser .readObject ();
242
+ if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo ) {
243
+ // Handle the case where the private key is encrypted.
244
+ PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo =
245
+ (PKCS8EncryptedPrivateKeyInfo ) pemObject ;
246
+ InputDecryptorProvider pkcs8Prov =
247
+ new JceOpenSSLPKCS8DecryptorProviderBuilder ().build (privateKeyFilePwd .toCharArray ());
248
+ privateKeyInfo = encryptedPrivateKeyInfo .decryptPrivateKeyInfo (pkcs8Prov );
249
+ } else if (pemObject instanceof PrivateKeyInfo ) {
250
+ // Handle the case where the private key is unencrypted.
251
+ privateKeyInfo = (PrivateKeyInfo ) pemObject ;
252
+ }
253
+ pemParser .close ();
254
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter ().setProvider (BOUNCY_CASTLE_PROVIDER );
255
+ return converter .getPrivateKey (privateKeyInfo );
256
+ }
257
+
258
+ private PrivateKey extractPrivateKeyWithJdk (String privateKeyFile , String privateKeyFilePwd )
259
+ throws IOException , NoSuchAlgorithmException , InvalidKeySpecException , InvalidKeyException {
260
+ String privateKeyContent = new String (Files .readAllBytes (Paths .get (privateKeyFile )));
261
+ if (Strings .isNullOrEmpty (privateKeyFilePwd )) {
262
+ // unencrypted private key file
263
+ PemReader pr = new PemReader (new StringReader (privateKeyContent ));
264
+ byte [] decoded = pr .readPemObject ().getContent ();
265
+ pr .close ();
266
+ PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec (decoded );
267
+ KeyFactory keyFactory = getKeyFactoryInstance ();
268
+ return keyFactory .generatePrivate (encodedKeySpec );
269
+ } else {
270
+ // encrypted private key file
271
+ PemReader pr = new PemReader (new StringReader (privateKeyContent ));
272
+ byte [] decoded = pr .readPemObject ().getContent ();
273
+ pr .close ();
274
+ EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo (decoded );
275
+ PBEKeySpec keySpec = new PBEKeySpec (privateKeyFilePwd .toCharArray ());
276
+ SecretKeyFactory pbeKeyFactory = this .getSecretKeyFactory (pkInfo .getAlgName ());
277
+ PKCS8EncodedKeySpec encodedKeySpec = pkInfo .getKeySpec (pbeKeyFactory .generateSecret (keySpec ));
278
+ KeyFactory keyFactory = getKeyFactoryInstance ();
279
+ return keyFactory .generatePrivate (encodedKeySpec );
280
+ }
281
+ }
225
282
}
0 commit comments