001package com.box.sdk; 002 003import com.eclipsesource.json.JsonObject; 004import java.io.IOException; 005import java.io.StringReader; 006import java.net.MalformedURLException; 007import java.net.URL; 008import java.security.PrivateKey; 009import java.security.Security; 010import java.text.ParseException; 011import java.text.SimpleDateFormat; 012import java.util.Date; 013import java.util.List; 014import java.util.logging.Level; 015import java.util.logging.Logger; 016import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 017import org.bouncycastle.jce.provider.BouncyCastleProvider; 018import org.bouncycastle.openssl.PEMDecryptorProvider; 019import org.bouncycastle.openssl.PEMEncryptedKeyPair; 020import org.bouncycastle.openssl.PEMKeyPair; 021import org.bouncycastle.openssl.PEMParser; 022import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 023import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; 024import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; 025import org.bouncycastle.operator.InputDecryptorProvider; 026import org.bouncycastle.operator.OperatorCreationException; 027import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; 028import org.bouncycastle.pkcs.PKCSException; 029import org.jose4j.jws.AlgorithmIdentifiers; 030import org.jose4j.jws.JsonWebSignature; 031import org.jose4j.jwt.JwtClaims; 032import org.jose4j.jwt.NumericDate; 033import org.jose4j.lang.JoseException; 034 035/** 036 * Represents an authenticated Box Developer Edition connection to the Box API. 037 * 038 * <p>This class handles everything for Box Developer Edition that isn't already handled by BoxAPIConnection.</p> 039 */ 040public class BoxDeveloperEditionAPIConnection extends BoxAPIConnection { 041 042 private static final String JWT_AUDIENCE = "https://api.box.com/oauth2/token"; 043 private static final String JWT_GRANT_TYPE = 044 "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&client_id=%s&client_secret=%s&assertion=%s"; 045 046 static { 047 Security.addProvider(new BouncyCastleProvider()); 048 } 049 050 private final String entityID; 051 private final DeveloperEditionEntityType entityType; 052 private final EncryptionAlgorithm encryptionAlgorithm; 053 private final String publicKeyID; 054 private final String privateKey; 055 private final String privateKeyPassword; 056 private BackoffCounter backoffCounter; 057 private IAccessTokenCache accessTokenCache; 058 059 /** 060 * Disabling an invalid constructor for Box Developer Edition. 061 * 062 * @param accessToken an initial access token to use for authenticating with the API. 063 */ 064 private BoxDeveloperEditionAPIConnection(String accessToken) { 065 super(accessToken); 066 throw new BoxAPIException("This constructor is not available for BoxDeveloperEditionAPIConnection."); 067 } 068 069 /** 070 * Disabling an invalid constructor for Box Developer Edition. 071 * 072 * @param clientID the client ID to use when refreshing the access token. 073 * @param clientSecret the client secret to use when refreshing the access token. 074 * @param accessToken an initial access token to use for authenticating with the API. 075 * @param refreshToken an initial refresh token to use when refreshing the access token. 076 */ 077 private BoxDeveloperEditionAPIConnection(String clientID, String clientSecret, String accessToken, 078 String refreshToken) { 079 super(accessToken); 080 throw new BoxAPIException("This constructor is not available for BoxDeveloperEditionAPIConnection."); 081 } 082 083 /** 084 * Disabling an invalid constructor for Box Developer Edition. 085 * 086 * @param clientID the client ID to use when exchanging the auth code for an access token. 087 * @param clientSecret the client secret to use when exchanging the auth code for an access token. 088 * @param authCode an auth code obtained from the first half of the OAuth process. 089 */ 090 private BoxDeveloperEditionAPIConnection(String clientID, String clientSecret, String authCode) { 091 super(clientID, clientSecret, authCode); 092 throw new BoxAPIException("This constructor is not available for BoxDeveloperEditionAPIConnection."); 093 } 094 095 /** 096 * Disabling an invalid constructor for Box Developer Edition. 097 * 098 * @param clientID the client ID to use when requesting an access token. 099 * @param clientSecret the client secret to use when requesting an access token. 100 */ 101 private BoxDeveloperEditionAPIConnection(String clientID, String clientSecret) { 102 super(clientID, clientSecret); 103 throw new BoxAPIException("This constructor is not available for BoxDeveloperEditionAPIConnection."); 104 } 105 106 /** 107 * Constructs a new BoxDeveloperEditionAPIConnection. 108 * 109 * @param entityId enterprise ID or a user ID. 110 * @param entityType the type of entityId. 111 * @param clientID the client ID to use when exchanging the JWT assertion for an access token. 112 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 113 * @param encryptionPref the encryption preferences for signing the JWT. 114 * @deprecated Use the version of this constructor that accepts an IAccessTokenCache to prevent unneeded 115 * requests to Box for access tokens. 116 */ 117 @Deprecated 118 public BoxDeveloperEditionAPIConnection( 119 String entityId, 120 DeveloperEditionEntityType entityType, 121 String clientID, 122 String clientSecret, 123 JWTEncryptionPreferences encryptionPref 124 ) { 125 126 this(entityId, entityType, clientID, clientSecret, encryptionPref, null); 127 } 128 129 130 /** 131 * Constructs a new BoxDeveloperEditionAPIConnection leveraging an access token cache. 132 * 133 * @param entityId enterprise ID or a user ID. 134 * @param entityType the type of entityId. 135 * @param clientID the client ID to use when exchanging the JWT assertion for an access token. 136 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 137 * @param encryptionPref the encryption preferences for signing the JWT. 138 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 139 */ 140 public BoxDeveloperEditionAPIConnection(String entityId, DeveloperEditionEntityType entityType, 141 String clientID, String clientSecret, 142 JWTEncryptionPreferences encryptionPref, 143 IAccessTokenCache accessTokenCache) { 144 145 super(clientID, clientSecret); 146 147 this.entityID = entityId; 148 this.entityType = entityType; 149 this.publicKeyID = encryptionPref.getPublicKeyID(); 150 this.privateKey = encryptionPref.getPrivateKey(); 151 this.privateKeyPassword = encryptionPref.getPrivateKeyPassword(); 152 this.encryptionAlgorithm = encryptionPref.getEncryptionAlgorithm(); 153 this.accessTokenCache = accessTokenCache; 154 this.backoffCounter = new BackoffCounter(new Time()); 155 } 156 157 /** 158 * Constructs a new BoxDeveloperEditionAPIConnection. 159 * 160 * @param entityId enterprise ID or a user ID. 161 * @param entityType the type of entityId. 162 * @param boxConfig box configuration settings object 163 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 164 */ 165 public BoxDeveloperEditionAPIConnection(String entityId, DeveloperEditionEntityType entityType, 166 BoxConfig boxConfig, IAccessTokenCache accessTokenCache) { 167 168 this(entityId, entityType, boxConfig.getClientId(), boxConfig.getClientSecret(), 169 boxConfig.getJWTEncryptionPreferences(), accessTokenCache); 170 } 171 172 /** 173 * Creates a new Box Developer Edition connection with enterprise token. 174 * 175 * @param enterpriseId the enterprise ID to use for requesting access token. 176 * @param clientId the client ID to use when exchanging the JWT assertion for an access token. 177 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 178 * @param encryptionPref the encryption preferences for signing the JWT. 179 * @return a new instance of BoxAPIConnection. 180 * @deprecated Use the version of this method that accepts an IAccessTokenCache to prevent unneeded 181 * requests to Box for access tokens. 182 */ 183 @Deprecated 184 public static BoxDeveloperEditionAPIConnection getAppEnterpriseConnection( 185 String enterpriseId, 186 String clientId, 187 String clientSecret, 188 JWTEncryptionPreferences encryptionPref 189 ) { 190 191 BoxDeveloperEditionAPIConnection connection = new BoxDeveloperEditionAPIConnection(enterpriseId, 192 DeveloperEditionEntityType.ENTERPRISE, clientId, clientSecret, encryptionPref); 193 194 connection.authenticate(); 195 196 return connection; 197 } 198 199 /** 200 * Creates a new Box Developer Edition connection with enterprise token leveraging an access token cache. 201 * 202 * @param enterpriseId the enterprise ID to use for requesting access token. 203 * @param clientId the client ID to use when exchanging the JWT assertion for an access token. 204 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 205 * @param encryptionPref the encryption preferences for signing the JWT. 206 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 207 * @return a new instance of BoxAPIConnection. 208 */ 209 public static BoxDeveloperEditionAPIConnection getAppEnterpriseConnection( 210 String enterpriseId, 211 String clientId, 212 String clientSecret, 213 JWTEncryptionPreferences encryptionPref, 214 IAccessTokenCache accessTokenCache 215 ) { 216 217 BoxDeveloperEditionAPIConnection connection = new BoxDeveloperEditionAPIConnection(enterpriseId, 218 DeveloperEditionEntityType.ENTERPRISE, clientId, clientSecret, encryptionPref, accessTokenCache); 219 220 connection.tryRestoreUsingAccessTokenCache(); 221 222 return connection; 223 } 224 225 /** 226 * Creates a new Box Developer Edition connection with enterprise token leveraging BoxConfig. 227 * 228 * @param boxConfig box configuration settings object 229 * @return a new instance of BoxAPIConnection. 230 */ 231 public static BoxDeveloperEditionAPIConnection getAppEnterpriseConnection(BoxConfig boxConfig) { 232 233 BoxDeveloperEditionAPIConnection connection = getAppEnterpriseConnection(boxConfig.getEnterpriseId(), 234 boxConfig.getClientId(), boxConfig.getClientSecret(), boxConfig.getJWTEncryptionPreferences()); 235 236 return connection; 237 } 238 239 /** 240 * Creates a new Box Developer Edition connection with enterprise token leveraging BoxConfig and access token cache. 241 * 242 * @param boxConfig box configuration settings object 243 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 244 * @return a new instance of BoxAPIConnection. 245 */ 246 public static BoxDeveloperEditionAPIConnection getAppEnterpriseConnection(BoxConfig boxConfig, 247 IAccessTokenCache accessTokenCache) { 248 249 BoxDeveloperEditionAPIConnection connection = getAppEnterpriseConnection(boxConfig.getEnterpriseId(), 250 boxConfig.getClientId(), boxConfig.getClientSecret(), boxConfig.getJWTEncryptionPreferences(), 251 accessTokenCache); 252 253 return connection; 254 } 255 256 /** 257 * Creates a new Box Developer Edition connection with App User token. 258 * 259 * @param userId the user ID to use for an App User. 260 * @param clientId the client ID to use when exchanging the JWT assertion for an access token. 261 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 262 * @param encryptionPref the encryption preferences for signing the JWT. 263 * @return a new instance of BoxAPIConnection. 264 * @deprecated Use the version of this method that accepts an IAccessTokenCache to prevent unneeded 265 * requests to Box for access tokens. 266 */ 267 @Deprecated 268 public static BoxDeveloperEditionAPIConnection getAppUserConnection( 269 String userId, 270 String clientId, 271 String clientSecret, 272 JWTEncryptionPreferences encryptionPref 273 ) { 274 275 BoxDeveloperEditionAPIConnection connection = new BoxDeveloperEditionAPIConnection( 276 userId, 277 DeveloperEditionEntityType.USER, 278 clientId, 279 clientSecret, 280 encryptionPref 281 ); 282 283 connection.authenticate(); 284 285 return connection; 286 } 287 288 /** 289 * Creates a new Box Developer Edition connection with App User token. 290 * 291 * @param userId the user ID to use for an App User. 292 * @param clientId the client ID to use when exchanging the JWT assertion for an access token. 293 * @param clientSecret the client secret to use when exchanging the JWT assertion for an access token. 294 * @param encryptionPref the encryption preferences for signing the JWT. 295 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 296 * @return a new instance of BoxAPIConnection. 297 */ 298 public static BoxDeveloperEditionAPIConnection getAppUserConnection( 299 String userId, 300 String clientId, 301 String clientSecret, 302 JWTEncryptionPreferences encryptionPref, 303 IAccessTokenCache accessTokenCache 304 ) { 305 306 BoxDeveloperEditionAPIConnection connection = new BoxDeveloperEditionAPIConnection(userId, 307 DeveloperEditionEntityType.USER, clientId, clientSecret, encryptionPref, accessTokenCache); 308 309 connection.tryRestoreUsingAccessTokenCache(); 310 311 return connection; 312 } 313 314 /** 315 * Creates a new Box Developer Edition connection with App User token levaraging BoxConfig. 316 * 317 * @param userId the user ID to use for an App User. 318 * @param boxConfig box configuration settings object 319 * @return a new instance of BoxAPIConnection. 320 */ 321 public static BoxDeveloperEditionAPIConnection getAppUserConnection(String userId, BoxConfig boxConfig) { 322 return getAppUserConnection(userId, boxConfig.getClientId(), boxConfig.getClientSecret(), 323 boxConfig.getJWTEncryptionPreferences()); 324 } 325 326 /** 327 * Creates a new Box Developer Edition connection with App User token leveraging BoxConfig and access token cache. 328 * 329 * @param userId the user ID to use for an App User. 330 * @param boxConfig box configuration settings object 331 * @param accessTokenCache the cache for storing access token information (to minimize fetching new tokens) 332 * @return a new instance of BoxAPIConnection. 333 */ 334 public static BoxDeveloperEditionAPIConnection getAppUserConnection(String userId, BoxConfig boxConfig, 335 IAccessTokenCache accessTokenCache) { 336 return getAppUserConnection(userId, boxConfig.getClientId(), boxConfig.getClientSecret(), 337 boxConfig.getJWTEncryptionPreferences(), accessTokenCache); 338 } 339 340 /** 341 * Disabling the non-Box Developer Edition authenticate method. 342 * 343 * @param authCode an auth code obtained from the first half of the OAuth process. 344 */ 345 public void authenticate(String authCode) { 346 throw new BoxAPIException("BoxDeveloperEditionAPIConnection does not allow authenticating with an auth code."); 347 } 348 349 /** 350 * Authenticates the API connection for Box Developer Edition. 351 */ 352 public void authenticate() { 353 URL url; 354 try { 355 url = new URL(this.getTokenURL()); 356 } catch (MalformedURLException e) { 357 assert false : "An invalid token URL indicates a bug in the SDK."; 358 throw new RuntimeException("An invalid token URL indicates a bug in the SDK.", e); 359 } 360 361 this.backoffCounter.reset(this.getMaxRetryAttempts() + 1); 362 NumericDate jwtTime = null; 363 String jwtAssertion; 364 String urlParameters; 365 BoxAPIRequest request; 366 String json = null; 367 final Logger logger = Logger.getLogger(BoxAPIRequest.class.getName()); 368 369 while (this.backoffCounter.getAttemptsRemaining() > 0) { 370 // Reconstruct the JWT assertion, which regenerates the jti claim, with the new "current" time 371 jwtAssertion = this.constructJWTAssertion(jwtTime); 372 urlParameters = String.format(JWT_GRANT_TYPE, this.getClientID(), this.getClientSecret(), jwtAssertion); 373 374 request = new BoxAPIRequest(this, url, "POST"); 375 request.shouldAuthenticate(false); 376 request.setBody(urlParameters); 377 378 try { 379 BoxJSONResponse response = (BoxJSONResponse) request.sendWithoutRetry(); 380 json = response.getJSON(); 381 break; 382 } catch (BoxAPIException apiException) { 383 long responseReceivedTime = System.currentTimeMillis(); 384 385 if (!this.backoffCounter.decrement() 386 || (!BoxAPIRequest.isRequestRetryable(apiException) 387 && !BoxAPIRequest.isResponseRetryable(apiException.getResponseCode(), apiException))) { 388 throw apiException; 389 } 390 391 logger.log(Level.WARNING, "Retrying authentication request due to transient error status={0} body={1}", 392 new Object[]{apiException.getResponseCode(), apiException.getResponse()}); 393 394 try { 395 List<String> retryAfterHeader = apiException.getHeaders().get("Retry-After"); 396 if (retryAfterHeader == null) { 397 this.backoffCounter.waitBackoff(); 398 } else { 399 int retryAfterDelay = Integer.parseInt(retryAfterHeader.get(0)) * 1000; 400 this.backoffCounter.waitBackoff(retryAfterDelay); 401 } 402 } catch (InterruptedException interruptedException) { 403 Thread.currentThread().interrupt(); 404 throw apiException; 405 } 406 407 long endWaitTime = System.currentTimeMillis(); 408 long secondsSinceResponseReceived = (endWaitTime - responseReceivedTime) / 1000; 409 410 try { 411 // Use the Date advertised by the Box server in the exception 412 // as the current time to synchronize clocks 413 jwtTime = this.getDateForJWTConstruction(apiException, secondsSinceResponseReceived); 414 } catch (Exception e) { 415 throw apiException; 416 } 417 418 } 419 } 420 421 if (json == null) { 422 throw new RuntimeException("Unable to read authentication response in SDK."); 423 } 424 425 JsonObject jsonObject = JsonObject.readFrom(json); 426 this.setAccessToken(jsonObject.get("access_token").asString()); 427 this.setLastRefresh(System.currentTimeMillis()); 428 this.setExpires(jsonObject.get("expires_in").asLong() * 1000); 429 430 //if token cache is specified, save to cache 431 if (this.accessTokenCache != null) { 432 String key = this.getAccessTokenCacheKey(); 433 JsonObject accessTokenCacheInfo = new JsonObject() 434 .add("accessToken", this.getAccessToken()) 435 .add("lastRefresh", this.getLastRefresh()) 436 .add("expires", this.getExpires()); 437 438 this.accessTokenCache.put(key, accessTokenCacheInfo.toString()); 439 } 440 } 441 442 private NumericDate getDateForJWTConstruction(BoxAPIException apiException, long secondsSinceResponseDateReceived) { 443 NumericDate currentTime; 444 List<String> responseDates = apiException.getHeaders().get("Date"); 445 446 if (responseDates != null) { 447 String responseDate = responseDates.get(0); 448 SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz"); 449 try { 450 Date date = dateFormat.parse(responseDate); 451 currentTime = NumericDate.fromMilliseconds(date.getTime()); 452 currentTime.addSeconds(secondsSinceResponseDateReceived); 453 } catch (ParseException e) { 454 currentTime = NumericDate.now(); 455 } 456 } else { 457 currentTime = NumericDate.now(); 458 } 459 return currentTime; 460 } 461 462 void setBackoffCounter(BackoffCounter counter) { 463 this.backoffCounter = counter; 464 } 465 466 /** 467 * BoxDeveloperEditionAPIConnection can always refresh, but this method is required elsewhere. 468 * 469 * @return true always. 470 */ 471 public boolean canRefresh() { 472 return true; 473 } 474 475 /** 476 * Refresh's this connection's access token using Box Developer Edition. 477 * 478 * @throws IllegalStateException if this connection's access token cannot be refreshed. 479 */ 480 public void refresh() { 481 this.getRefreshLock().writeLock().lock(); 482 483 try { 484 this.authenticate(); 485 } catch (BoxAPIException e) { 486 this.notifyError(e); 487 this.getRefreshLock().writeLock().unlock(); 488 throw e; 489 } 490 491 this.notifyRefresh(); 492 this.getRefreshLock().writeLock().unlock(); 493 } 494 495 private String getAccessTokenCacheKey() { 496 return String.format("/%s/%s/%s/%s", this.getUserAgent(), this.getClientID(), 497 this.entityType.toString(), this.entityID); 498 } 499 500 private void tryRestoreUsingAccessTokenCache() { 501 if (this.accessTokenCache == null) { 502 //no cache specified so force authentication 503 this.authenticate(); 504 } else { 505 String cachedTokenInfo = this.accessTokenCache.get(this.getAccessTokenCacheKey()); 506 if (cachedTokenInfo == null) { 507 //not found; probably first time for this client config so authenticate; info will then be cached 508 this.authenticate(); 509 } else { 510 //pull access token cache info; authentication will occur as needed (if token is expired) 511 JsonObject json = JsonObject.readFrom(cachedTokenInfo); 512 this.setAccessToken(json.get("accessToken").asString()); 513 this.setLastRefresh(json.get("lastRefresh").asLong()); 514 this.setExpires(json.get("expires").asLong()); 515 } 516 } 517 } 518 519 private String constructJWTAssertion() { 520 return this.constructJWTAssertion(null); 521 } 522 523 private String constructJWTAssertion(NumericDate now) { 524 JwtClaims claims = new JwtClaims(); 525 claims.setIssuer(this.getClientID()); 526 claims.setAudience(JWT_AUDIENCE); 527 if (now == null) { 528 claims.setExpirationTimeMinutesInTheFuture(0.5f); 529 } else { 530 now.addSeconds(30L); 531 claims.setExpirationTime(now); 532 } 533 claims.setSubject(this.entityID); 534 claims.setClaim("box_sub_type", this.entityType.toString()); 535 claims.setGeneratedJwtId(64); 536 537 JsonWebSignature jws = new JsonWebSignature(); 538 jws.setPayload(claims.toJson()); 539 jws.setKey(this.decryptPrivateKey()); 540 jws.setAlgorithmHeaderValue(this.getAlgorithmIdentifier()); 541 jws.setHeader("typ", "JWT"); 542 if ((this.publicKeyID != null) && !this.publicKeyID.isEmpty()) { 543 jws.setHeader("kid", this.publicKeyID); 544 } 545 546 String assertion; 547 548 try { 549 assertion = jws.getCompactSerialization(); 550 } catch (JoseException e) { 551 throw new BoxAPIException("Error serializing JSON Web Token assertion.", e); 552 } 553 554 return assertion; 555 } 556 557 private String getAlgorithmIdentifier() { 558 String algorithmId = AlgorithmIdentifiers.RSA_USING_SHA256; 559 switch (this.encryptionAlgorithm) { 560 case RSA_SHA_384: 561 algorithmId = AlgorithmIdentifiers.RSA_USING_SHA384; 562 break; 563 case RSA_SHA_512: 564 algorithmId = AlgorithmIdentifiers.RSA_USING_SHA512; 565 break; 566 case RSA_SHA_256: 567 default: 568 break; 569 } 570 571 return algorithmId; 572 } 573 574 private PrivateKey decryptPrivateKey() { 575 PrivateKey decryptedPrivateKey = null; 576 try { 577 PEMParser keyReader = new PEMParser(new StringReader(this.privateKey)); 578 Object keyPair = keyReader.readObject(); 579 keyReader.close(); 580 581 if (keyPair instanceof PrivateKeyInfo) { 582 PrivateKeyInfo keyInfo = (PrivateKeyInfo) keyPair; 583 decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo); 584 } else if (keyPair instanceof PEMEncryptedKeyPair) { 585 JcePEMDecryptorProviderBuilder builder = new JcePEMDecryptorProviderBuilder(); 586 PEMDecryptorProvider decryptionProvider = builder.build(this.privateKeyPassword.toCharArray()); 587 keyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProvider); 588 PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo(); 589 decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo); 590 } else if (keyPair instanceof PKCS8EncryptedPrivateKeyInfo) { 591 InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC") 592 .build(this.privateKeyPassword.toCharArray()); 593 PrivateKeyInfo keyInfo = ((PKCS8EncryptedPrivateKeyInfo) keyPair).decryptPrivateKeyInfo(pkcs8Prov); 594 decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo); 595 } else { 596 PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo(); 597 decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo); 598 } 599 } catch (IOException e) { 600 throw new BoxAPIException("Error parsing private key for Box Developer Edition.", e); 601 } catch (OperatorCreationException e) { 602 throw new BoxAPIException("Error parsing PKCS#8 private key for Box Developer Edition.", e); 603 } catch (PKCSException e) { 604 throw new BoxAPIException("Error parsing PKCS private key for Box Developer Edition.", e); 605 } 606 return decryptedPrivateKey; 607 } 608 609}