Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Introduce IPrivateKeyDecryptor to allow using custom cryptography provider #1226

Merged
merged 7 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/main/java/com/box/sdk/BCPrivateKeyDecryptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.box.sdk;

import java.io.IOException;
import java.io.StringReader;
import java.security.PrivateKey;
import java.security.Security;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;

lukaszsocha2 marked this conversation as resolved.
Show resolved Hide resolved

public class BCPrivateKeyDecryptor implements IPrivateKeyDecryptor {

public BCPrivateKeyDecryptor() {
Security.addProvider(new BouncyCastleProvider());
}

@Override
public PrivateKey decryptPrivateKey(String encryptedPrivateKey, String passphrase) {
PrivateKey decryptedPrivateKey;
try {
PEMParser keyReader = new PEMParser(new StringReader(encryptedPrivateKey));
Object keyPair = keyReader.readObject();
keyReader.close();

if (keyPair instanceof PrivateKeyInfo) {
PrivateKeyInfo keyInfo = (PrivateKeyInfo) keyPair;
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else if (keyPair instanceof PEMEncryptedKeyPair) {
JcePEMDecryptorProviderBuilder builder = new JcePEMDecryptorProviderBuilder();
PEMDecryptorProvider decryptionProvider = builder.build(passphrase.toCharArray());
keyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProvider);
PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else if (keyPair instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC")
.build(passphrase.toCharArray());
PrivateKeyInfo keyInfo = ((PKCS8EncryptedPrivateKeyInfo) keyPair).decryptPrivateKeyInfo(pkcs8Prov);
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else {
PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
}
} catch (IOException e) {
throw new BoxAPIException("Error parsing private key for Box Developer Edition.", e);
} catch (OperatorCreationException e) {
throw new BoxAPIException("Error parsing PKCS#8 private key for Box Developer Edition.", e);
} catch (PKCSException e) {
throw new BoxAPIException("Error parsing PKCS private key for Box Developer Edition.", e);
}
return decryptedPrivateKey;
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/box/sdk/BoxConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,8 @@ public String getClientId() {
public void setClientId(String clientId) {
this.clientId = clientId;
}

public void setPrivateKeyDecryptor(IPrivateKeyDecryptor privateKeyDecryptor) {
this.jwtEncryptionPreferences.setPrivateKeyDecryptor(privateKeyDecryptor);
}
}
60 changes: 4 additions & 56 deletions src/main/java/com/box/sdk/BoxDeveloperEditionAPIConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,12 @@

import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.PrivateKey;
import java.security.Security;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
Expand All @@ -43,10 +26,6 @@ public class BoxDeveloperEditionAPIConnection extends BoxAPIConnection {
"grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&client_id=%s&client_secret=%s&assertion=%s";
private static final int DEFAULT_MAX_ENTRIES = 100;

static {
Security.addProvider(new BouncyCastleProvider());
}

private final String entityID;
private final DeveloperEditionEntityType entityType;
private final EncryptionAlgorithm encryptionAlgorithm;
Expand All @@ -55,6 +34,7 @@ public class BoxDeveloperEditionAPIConnection extends BoxAPIConnection {
private final String privateKeyPassword;
private BackoffCounter backoffCounter;
private final IAccessTokenCache accessTokenCache;
private final IPrivateKeyDecryptor privateKeyDecryptor;

/**
* Constructs a new BoxDeveloperEditionAPIConnection leveraging an access token cache.
Expand All @@ -79,6 +59,7 @@ public BoxDeveloperEditionAPIConnection(String entityId, DeveloperEditionEntityT
this.privateKey = encryptionPref.getPrivateKey();
this.privateKeyPassword = encryptionPref.getPrivateKeyPassword();
this.encryptionAlgorithm = encryptionPref.getEncryptionAlgorithm();
this.privateKeyDecryptor = encryptionPref.getPrivateKeyDecryptor();
this.accessTokenCache = accessTokenCache;
this.backoffCounter = new BackoffCounter(new Time());
}
Expand Down Expand Up @@ -500,7 +481,7 @@ private String constructJWTAssertion(NumericDate now) {

JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(this.decryptPrivateKey());
jws.setKey(this.privateKeyDecryptor.decryptPrivateKey(this.privateKey, this.privateKeyPassword));
jws.setAlgorithmHeaderValue(this.getAlgorithmIdentifier());
jws.setHeader("typ", "JWT");
if ((this.publicKeyID != null) && !this.publicKeyID.isEmpty()) {
Expand Down Expand Up @@ -535,39 +516,6 @@ private String getAlgorithmIdentifier() {
return algorithmId;
}

private PrivateKey decryptPrivateKey() {
PrivateKey decryptedPrivateKey;
try {
PEMParser keyReader = new PEMParser(new StringReader(this.privateKey));
Object keyPair = keyReader.readObject();
keyReader.close();

if (keyPair instanceof PrivateKeyInfo) {
PrivateKeyInfo keyInfo = (PrivateKeyInfo) keyPair;
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else if (keyPair instanceof PEMEncryptedKeyPair) {
JcePEMDecryptorProviderBuilder builder = new JcePEMDecryptorProviderBuilder();
PEMDecryptorProvider decryptionProvider = builder.build(this.privateKeyPassword.toCharArray());
keyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProvider);
PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else if (keyPair instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC")
.build(this.privateKeyPassword.toCharArray());
PrivateKeyInfo keyInfo = ((PKCS8EncryptedPrivateKeyInfo) keyPair).decryptPrivateKeyInfo(pkcs8Prov);
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
} else {
PrivateKeyInfo keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
decryptedPrivateKey = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo);
}
} catch (IOException e) {
throw new BoxAPIException("Error parsing private key for Box Developer Edition.", e);
} catch (OperatorCreationException e) {
throw new BoxAPIException("Error parsing PKCS#8 private key for Box Developer Edition.", e);
} catch (PKCSException e) {
throw new BoxAPIException("Error parsing PKCS private key for Box Developer Edition.", e);
}
return decryptedPrivateKey;
}


}
8 changes: 8 additions & 0 deletions src/main/java/com/box/sdk/IPrivateKeyDecryptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.box.sdk;

import java.security.PrivateKey;

public interface IPrivateKeyDecryptor {

PrivateKey decryptPrivateKey(String encryptedPrivateKey, String passphrase);
}
10 changes: 10 additions & 0 deletions src/main/java/com/box/sdk/JWTEncryptionPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class JWTEncryptionPreferences {
private String privateKey;
private String privateKeyPassword;
private EncryptionAlgorithm encryptionAlgorithm;
private IPrivateKeyDecryptor privateKeyDecryptor = new BCPrivateKeyDecryptor();


/**
* Returns the ID for public key for validating the JWT signature.
Expand Down Expand Up @@ -81,4 +83,12 @@ public EncryptionAlgorithm getEncryptionAlgorithm() {
public void setEncryptionAlgorithm(EncryptionAlgorithm encryptionAlgorithm) {
this.encryptionAlgorithm = encryptionAlgorithm;
}

public IPrivateKeyDecryptor getPrivateKeyDecryptor() {
return privateKeyDecryptor;
}

public void setPrivateKeyDecryptor(IPrivateKeyDecryptor privateKeyDecryptor) {
this.privateKeyDecryptor = privateKeyDecryptor;
}
}
Loading