certs impl
This commit is contained in:
parent
1a67ec773d
commit
6bf1551028
45 changed files with 810 additions and 311 deletions
|
@ -45,6 +45,7 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ProxyData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
|
@ -200,7 +201,7 @@ public class ClientHttpRequestFactoryService {
|
|||
.loadTrustMaterial(trustStoreFile, password)
|
||||
.setKeyStoreType(this.environment.getProperty(
|
||||
"server.ssl.key-store-type",
|
||||
"pkcs12"))
|
||||
Constants.PKCS_12))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -218,9 +219,11 @@ public class ClientHttpRequestFactoryService {
|
|||
.setSSLContext(sslContext)
|
||||
.build();
|
||||
final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
|
||||
|
||||
factory.setConnectionRequestTimeout(this.connectionRequestTimeout);
|
||||
factory.setConnectTimeout(this.connectTimeout);
|
||||
factory.setReadTimeout(this.readTimeout);
|
||||
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
@ -246,8 +249,10 @@ public class ClientHttpRequestFactoryService {
|
|||
if (proxy.clientCredentials != null && StringUtils.isNotBlank(proxy.clientCredentials.clientId)) {
|
||||
final CredentialsProvider credsProvider = new BasicCredentialsProvider();
|
||||
final String plainClientId = proxy.clientCredentials.clientIdAsString();
|
||||
final String plainClientSecret = Utils.toString(this.clientCredentialService
|
||||
.getPlainClientSecret(proxy.clientCredentials));
|
||||
final CharSequence secret = this.clientCredentialService
|
||||
.getPlainClientSecret(proxy.clientCredentials)
|
||||
.getOrThrow();
|
||||
final String plainClientSecret = Utils.toString(secret);
|
||||
|
||||
credsProvider.setCredentials(
|
||||
AuthScope.ANY,
|
||||
|
|
|
@ -130,6 +130,13 @@ public final class Constants {
|
|||
public static final int GZIP_CM = 8;
|
||||
|
||||
public static final String SHA_256 = "SHA-256";
|
||||
public static final String X_509 = "X.509";
|
||||
public static final String PKCS_12 = "pkcs12";
|
||||
public static final String SHA_1 = "SHA-1";
|
||||
public static final String AES = "AES";
|
||||
public static final String AES_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
|
||||
public static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||
public static final String KEY_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
|
||||
|
||||
public static final RGB WHITE_RGB = new RGB(255, 255, 255);
|
||||
public static final RGB BLACK_RGB = new RGB(0, 0, 0);
|
||||
|
|
|
@ -37,7 +37,7 @@ public interface ClientCredentialService {
|
|||
* @param secretPlaintext the plain secret text
|
||||
* @param accessTokenPlaintext the plain accessToken text
|
||||
* @return encrypted client credentials */
|
||||
ClientCredentials encryptClientCredentials(
|
||||
Result<ClientCredentials> encryptClientCredentials(
|
||||
CharSequence clientIdPlaintext,
|
||||
CharSequence secretPlaintext,
|
||||
CharSequence accessTokenPlaintext);
|
||||
|
@ -48,7 +48,7 @@ public interface ClientCredentialService {
|
|||
* @param clientIdPlaintext the plain clientId text
|
||||
* @param secretPlaintext the plain secret text
|
||||
* @return encrypted client credentials */
|
||||
default ClientCredentials encryptClientCredentials(
|
||||
default Result<ClientCredentials> encryptClientCredentials(
|
||||
final CharSequence clientIdPlaintext,
|
||||
final CharSequence secretPlaintext) {
|
||||
|
||||
|
@ -59,13 +59,13 @@ public interface ClientCredentialService {
|
|||
*
|
||||
* @param credentials ClientCredentials containing the secret to decrypt
|
||||
* @return decrypted plain text secret */
|
||||
CharSequence getPlainClientSecret(ClientCredentials credentials);
|
||||
Result<CharSequence> getPlainClientSecret(ClientCredentials credentials);
|
||||
|
||||
/** Use this to get a decrypted plain text accessToken form given ClientCredentials
|
||||
*
|
||||
* @param credentials ClientCredentials containing the accessToken to decrypt
|
||||
* @return decrypted plain text accessToken */
|
||||
CharSequence getPlainAccessToken(ClientCredentials credentials);
|
||||
Result<CharSequence> getPlainAccessToken(ClientCredentials credentials);
|
||||
|
||||
/** Encrypts a given plain text that uses {@link org.springframework.security.crypto.encrypt.Encryptors#stronger}
|
||||
* and a randomly generated salt that is added to the cipher text.
|
||||
|
@ -73,12 +73,12 @@ public interface ClientCredentialService {
|
|||
*
|
||||
* @param text the plain text to encrypt
|
||||
* @return encrypted cipher text with additional salt */
|
||||
CharSequence encrypt(final CharSequence text);
|
||||
Result<CharSequence> encrypt(final CharSequence text);
|
||||
|
||||
/** Decrypt a given cipher that was encrypted with the method used by this services encrypt method.
|
||||
*
|
||||
* @param cipher the cipher text with additional salt
|
||||
* @return plain text decrypt from the given cipher */
|
||||
CharSequence decrypt(final CharSequence cipher);
|
||||
Result<CharSequence> decrypt(final CharSequence cipher);
|
||||
|
||||
}
|
|
@ -13,8 +13,6 @@ import java.security.SecureRandom;
|
|||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -25,8 +23,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
@Service
|
||||
public class ClientCredentialServiceImpl implements ClientCredentialService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientCredentialServiceImpl.class);
|
||||
|
||||
private final Cryptor cryptor;
|
||||
|
||||
protected ClientCredentialServiceImpl(final Cryptor cryptor) {
|
||||
|
@ -35,38 +31,35 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
|
||||
@Override
|
||||
public Result<ClientCredentials> generatedClientCredentials() {
|
||||
return Result.tryCatch(() -> {
|
||||
try {
|
||||
|
||||
return encryptClientCredentials(
|
||||
generateClientId(),
|
||||
generateClientSecret());
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Error while trying to generate client credentials: ", e);
|
||||
throw new RuntimeException("cause: ", e);
|
||||
}
|
||||
});
|
||||
return encryptClientCredentials(
|
||||
generateClientId(),
|
||||
generateClientSecret());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientCredentials encryptClientCredentials(
|
||||
public Result<ClientCredentials> encryptClientCredentials(
|
||||
final CharSequence clientIdPlaintext,
|
||||
final CharSequence secretPlaintext,
|
||||
final CharSequence accessTokenPlaintext) {
|
||||
|
||||
return new ClientCredentials(
|
||||
clientIdPlaintext,
|
||||
(StringUtils.isNoneBlank(secretPlaintext))
|
||||
? this.cryptor.encrypt(secretPlaintext).toString()
|
||||
: null,
|
||||
(StringUtils.isNoneBlank(accessTokenPlaintext))
|
||||
? this.cryptor.encrypt(accessTokenPlaintext).toString()
|
||||
: null);
|
||||
return Result.tryCatch(() -> {
|
||||
return new ClientCredentials(
|
||||
clientIdPlaintext,
|
||||
(StringUtils.isNoneBlank(secretPlaintext))
|
||||
? this.cryptor.encrypt(secretPlaintext)
|
||||
.getOrThrow()
|
||||
.toString()
|
||||
: null,
|
||||
(StringUtils.isNoneBlank(accessTokenPlaintext))
|
||||
? this.cryptor.encrypt(accessTokenPlaintext)
|
||||
.getOrThrow()
|
||||
.toString()
|
||||
: null);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPlainClientSecret(final ClientCredentials credentials) {
|
||||
public Result<CharSequence> getPlainClientSecret(final ClientCredentials credentials) {
|
||||
if (credentials == null || !credentials.hasSecret()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -75,21 +68,21 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPlainAccessToken(final ClientCredentials credentials) {
|
||||
public Result<CharSequence> getPlainAccessToken(final ClientCredentials credentials) {
|
||||
if (credentials == null || !credentials.hasAccessToken()) {
|
||||
return null;
|
||||
return Result.ofRuntimeError("No token available");
|
||||
}
|
||||
|
||||
return this.cryptor.decrypt(credentials.accessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence encrypt(final CharSequence text) {
|
||||
public Result<CharSequence> encrypt(final CharSequence text) {
|
||||
return this.cryptor.encrypt(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence decrypt(final CharSequence text) {
|
||||
public Result<CharSequence> decrypt(final CharSequence text) {
|
||||
return this.cryptor.decrypt(text);
|
||||
}
|
||||
|
||||
|
@ -104,7 +97,6 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
}
|
||||
|
||||
public static CharSequence generateClientSecret() {
|
||||
// TODO find a better way to generate a random char array instead of using RandomStringUtils.random which uses a String
|
||||
return RandomStringUtils.random(
|
||||
64, 0, possibleCharacters.length - 1, false, false,
|
||||
possibleCharacters, new SecureRandom());
|
||||
|
|
|
@ -47,6 +47,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
public static final String ATTR_QUIT_PASSWORD = "hashedQuitPassword";
|
||||
public static final String ATTR_QUIT_PASSWORD_CONFIRM = "hashedQuitPasswordConfirm";
|
||||
public static final String ATTR_ENCRYPT_SECRET_CONFIRM = "confirm_encrypt_secret";
|
||||
public static final String ATTR_ENCRYPT_CERTIFICATE_ALIAS = "cert_alias";
|
||||
|
||||
public static final String FILTER_ATTR_CREATION_DATE = "creation_date";
|
||||
|
||||
|
@ -157,6 +158,9 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
@JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM)
|
||||
public final CharSequence encryptSecretConfirm;
|
||||
|
||||
@JsonProperty(ATTR_ENCRYPT_CERTIFICATE_ALIAS)
|
||||
public final String encryptCertificateAlias;
|
||||
|
||||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE)
|
||||
public final Boolean active;
|
||||
|
||||
|
@ -185,6 +189,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_DATE) final DateTime date,
|
||||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret,
|
||||
@JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM) final CharSequence encryptSecretConfirm,
|
||||
@JsonProperty(ATTR_ENCRYPT_CERTIFICATE_ALIAS) final String encryptCertificateAlias,
|
||||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active) {
|
||||
|
||||
this.id = id;
|
||||
|
@ -210,6 +215,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
this.date = date;
|
||||
this.encryptSecret = encryptSecret;
|
||||
this.encryptSecretConfirm = encryptSecretConfirm;
|
||||
this.encryptCertificateAlias = encryptCertificateAlias;
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
|
@ -247,6 +253,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
this.date = postParams.getDateTime(Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE);
|
||||
this.encryptSecret = postParams.getCharSequence(Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET);
|
||||
this.encryptSecretConfirm = postParams.getCharSequence(ATTR_ENCRYPT_SECRET_CONFIRM);
|
||||
this.encryptCertificateAlias = postParams.getString(ATTR_ENCRYPT_CERTIFICATE_ALIAS);
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
|
@ -331,6 +338,10 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
return this.encryptSecret;
|
||||
}
|
||||
|
||||
public String getEncryptCertificateAlias() {
|
||||
return this.encryptCertificateAlias;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public CharSequence getEncryptSecretConfirm() {
|
||||
return this.encryptSecretConfirm;
|
||||
|
@ -430,6 +441,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
this.date,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
this.active);
|
||||
}
|
||||
|
||||
|
@ -456,6 +468,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable {
|
|||
DateTime.now(DateTimeZone.UTC),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.gbl.util;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
import org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi;
|
||||
import org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.BCPKCS12KeyStore;
|
||||
|
@ -22,6 +24,7 @@ import org.springframework.security.crypto.encrypt.Encryptors;
|
|||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/** Cryptor dealing with internal encryption and decryption. */
|
||||
@Lazy
|
||||
@Service
|
||||
public class Cryptor {
|
||||
|
@ -34,11 +37,19 @@ public class Cryptor {
|
|||
this.internalPWD = environment.getProperty("sebserver.webservice.internalSecret");
|
||||
}
|
||||
|
||||
public CharSequence encrypt(final CharSequence text) {
|
||||
/** Use this to encrypt a text with the internal password
|
||||
*
|
||||
* @param text The text to encrypt with the internal password
|
||||
* @return the encrypted text cipher */
|
||||
public Result<CharSequence> encrypt(final CharSequence text) {
|
||||
return encrypt(text, this.internalPWD);
|
||||
}
|
||||
|
||||
public CharSequence decrypt(final CharSequence text) {
|
||||
/** Use this to decrypt a cipher text with the internal password
|
||||
*
|
||||
* @param text The cipher text to decrypt with the internal password
|
||||
* @return the plain text */
|
||||
public Result<CharSequence> decrypt(final CharSequence text) {
|
||||
return decrypt(text, this.internalPWD);
|
||||
}
|
||||
|
||||
|
@ -64,17 +75,40 @@ public class Cryptor {
|
|||
}
|
||||
}
|
||||
|
||||
public static CharSequence encrypt(final CharSequence text, final CharSequence secret) {
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("Text has null reference");
|
||||
}
|
||||
public Result<PKCS12KeyStoreSpi> addPrivateKey(
|
||||
final PKCS12KeyStoreSpi keyStore,
|
||||
final PrivateKey privateKey,
|
||||
final String alias,
|
||||
final Certificate certificate) {
|
||||
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip encryption");
|
||||
return text;
|
||||
}
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
try {
|
||||
keyStore.engineSetKeyEntry(
|
||||
alias,
|
||||
privateKey,
|
||||
Utils.toCharArray(this.internalPWD),
|
||||
new Certificate[] { certificate });
|
||||
|
||||
return keyStore;
|
||||
});
|
||||
}
|
||||
|
||||
public Result<PrivateKey> getPrivateKey(final PKCS12KeyStoreSpi store, final String alias) {
|
||||
return Result.tryCatch(() -> {
|
||||
return (PrivateKey) store.engineGetKey(alias, Utils.toCharArray(this.internalPWD));
|
||||
});
|
||||
}
|
||||
|
||||
static Result<CharSequence> encrypt(final CharSequence text, final CharSequence secret) {
|
||||
return Result.tryCatch(() -> {
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("Text has null reference");
|
||||
}
|
||||
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip encryption");
|
||||
return text;
|
||||
}
|
||||
|
||||
final CharSequence salt = KeyGenerators.string().generateKey();
|
||||
final CharSequence cipher = Encryptors
|
||||
|
@ -84,23 +118,19 @@ public class Cryptor {
|
|||
return new StringBuilder(cipher)
|
||||
.append(salt);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to encrypt text: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static CharSequence decrypt(final CharSequence cipher, final CharSequence secret) {
|
||||
if (cipher == null) {
|
||||
throw new IllegalArgumentException("Cipher has null reference");
|
||||
}
|
||||
static Result<CharSequence> decrypt(final CharSequence cipher, final CharSequence secret) {
|
||||
return Result.tryCatch(() -> {
|
||||
if (cipher == null) {
|
||||
throw new IllegalArgumentException("Cipher has null reference");
|
||||
}
|
||||
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip decryption");
|
||||
return cipher;
|
||||
}
|
||||
|
||||
try {
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip decryption");
|
||||
return cipher;
|
||||
}
|
||||
|
||||
final int length = cipher.length();
|
||||
final int cipherTextLength = length - 16;
|
||||
|
@ -111,9 +141,7 @@ public class Cryptor {
|
|||
.delux(secret, salt)
|
||||
.decrypt(cipherText.toString());
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to decrypt text: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -382,7 +382,8 @@ public final class Result<T> {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result [value=" + this.value + ", error=" + this.error + "]";
|
||||
throw new RuntimeException("!!!!!!!!!!!!!");
|
||||
//return "Result [value=" + this.value + ", error=" + this.error + "]";
|
||||
}
|
||||
|
||||
public interface TryCatchSupplier<T> {
|
||||
|
|
|
@ -15,8 +15,6 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -48,8 +46,6 @@ import ch.ethz.seb.sebserver.gui.widget.FileUploadSelection;
|
|||
@GuiProfile
|
||||
public class CertificateImportPopup {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SEBExamConfigImportPopup.class);
|
||||
|
||||
private final static PageMessageException MISSING_PASSWORD = new PageMessageException(
|
||||
new LocTextKey("sebserver.certificate.action.import.missing-password"));
|
||||
|
||||
|
|
|
@ -242,6 +242,11 @@ public class SEBClientConfigForm implements TemplateComposer {
|
|||
final boolean showVDIAttrs = clientConfig.vdiType == VDIType.VM_WARE;
|
||||
final boolean showFallbackAttrs = BooleanUtils.isTrue(clientConfig.fallback);
|
||||
|
||||
final CharSequence pwd = (formHandleAnchor.formHandle == null)
|
||||
? clientConfig.getEncryptSecret()
|
||||
: this.cryptor.encrypt(clientConfig.getEncryptSecret())
|
||||
.getOrThrow();
|
||||
|
||||
PageService.clearComposite(formContent);
|
||||
|
||||
final FormBuilder formBuilder = this.pageService.formBuilder(
|
||||
|
@ -282,9 +287,7 @@ public class SEBClientConfigForm implements TemplateComposer {
|
|||
.addField(FormBuilder.password(
|
||||
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET,
|
||||
FORM_ENCRYPT_SECRET_TEXT_KEY,
|
||||
(formHandleAnchor.formHandle == null)
|
||||
? clientConfig.getEncryptSecret()
|
||||
: this.cryptor.encrypt(clientConfig.getEncryptSecret())))
|
||||
pwd))
|
||||
|
||||
.withDefaultSpanEmptyCell(3)
|
||||
.addFieldIf(
|
||||
|
@ -292,9 +295,7 @@ public class SEBClientConfigForm implements TemplateComposer {
|
|||
() -> FormBuilder.password(
|
||||
SEBClientConfig.ATTR_ENCRYPT_SECRET_CONFIRM,
|
||||
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY,
|
||||
(formHandleAnchor.formHandle == null)
|
||||
? clientConfig.getEncryptSecret()
|
||||
: this.cryptor.encrypt(clientConfig.getEncryptSecretConfirm())))
|
||||
pwd))
|
||||
|
||||
.withDefaultSpanInput(2)
|
||||
.addField(FormBuilder.text(
|
||||
|
|
|
@ -320,7 +320,9 @@ public final class Form implements FormBinding {
|
|||
@Override public String getStringValue() {return pwdInput.getValue() != null ? pwdInput.getValue().toString() : null;}
|
||||
@Override public void setStringValue(final String value) {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
pwdInput.setValue(Form.this.cryptor.decrypt(value));
|
||||
final CharSequence pwd = Form.this.cryptor.decrypt(value)
|
||||
.getOrThrow();
|
||||
pwdInput.setValue(pwd);
|
||||
} else {
|
||||
pwdInput.setValue(value);
|
||||
}
|
||||
|
|
|
@ -8,22 +8,27 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.form;
|
||||
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.widget.PasswordInput;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.widget.PasswordInput;
|
||||
|
||||
public class PasswordFieldBuilder extends FieldBuilder<CharSequence> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PasswordFieldBuilder.class);
|
||||
|
||||
PasswordFieldBuilder(final String name, final LocTextKey label, final CharSequence value) {
|
||||
super(name, label, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
void build(FormBuilder builder) {
|
||||
void build(final FormBuilder builder) {
|
||||
final boolean readonly = builder.readonly || this.readonly;
|
||||
final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
|
||||
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
|
||||
|
@ -32,6 +37,8 @@ public class PasswordFieldBuilder extends FieldBuilder<CharSequence> {
|
|||
input.setEditable(!readonly);
|
||||
input.setValue((StringUtils.isNotBlank(this.value))
|
||||
? builder.cryptor.decrypt(this.value)
|
||||
.onError(error -> log.error("Failed to internally decrypt password: {}", error.getMessage()))
|
||||
.getOr(this.value)
|
||||
: this.value);
|
||||
|
||||
if (builder.pageService.getFormTooltipMode() == PageService.FormTooltipMode.INPUT) {
|
||||
|
|
|
@ -154,7 +154,9 @@ public class PasswordFieldBuilder implements InputFieldBuilder {
|
|||
@Override
|
||||
protected void setValueToControl(final String value) {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
final CharSequence pwd = this.cryptor.decrypt(value);
|
||||
final CharSequence pwd = this.cryptor
|
||||
.decrypt(value)
|
||||
.getOrThrow();
|
||||
this.control.setValue(pwd.toString());
|
||||
this.confirm.setValue(pwd.toString());
|
||||
} else {
|
||||
|
|
|
@ -36,7 +36,7 @@ public class RemoveCertificate extends RestCall<Collection<EntityKey>> {
|
|||
}),
|
||||
HttpMethod.DELETE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.CERTIFICATE_ENDPOINT + API.CERTIFICATE_ALIAS_VAR_PATH_SEGMENT);
|
||||
API.CERTIFICATE_ENDPOINT);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
|
@ -31,12 +33,25 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
/** Concrete EntityDAO interface of Certificate entities */
|
||||
public interface CertificateDAO {
|
||||
|
||||
Result<Certificate> getCertificate(final Long institutionId, String alias);
|
||||
|
||||
Result<Certificates> getCertificates(Long institutionId);
|
||||
|
||||
Result<CertificateInfo> addCertificate(Long institutionId, String alias, Certificate certificate);
|
||||
Result<CertificateInfo> addCertificate(
|
||||
Long institutionId,
|
||||
String alias,
|
||||
Certificate certificate);
|
||||
|
||||
Result<CertificateInfo> addCertificate(
|
||||
Long institutionId,
|
||||
String alias,
|
||||
Certificate certificate,
|
||||
PrivateKey privateKey);
|
||||
|
||||
Result<EntityKey> removeCertificate(Long institutionId, String alias);
|
||||
|
||||
Result<Collection<String>> getAllIdentityAlias(Long institutionId);
|
||||
|
||||
static Result<CertificateInfo> getDataFromCertificate(final Certificates certificates, final String alias) {
|
||||
return Result.tryCatch(() -> {
|
||||
final X509Certificate certificate = (X509Certificate) certificates.keyStore.engineGetCertificate(alias);
|
||||
|
|
|
@ -10,12 +10,14 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -64,7 +66,19 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Certificate> getCertificate(final Long institutionId, final String alias) {
|
||||
return getCertificates(institutionId)
|
||||
.map(certs -> {
|
||||
if (!certs.aliases.contains(alias)) {
|
||||
throw new ResourceNotFoundException(EntityType.CERTIFICATE, alias);
|
||||
}
|
||||
return certs.keyStore.engineGetCertificate(alias);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Certificates> getCertificates(final Long institutionId) {
|
||||
|
||||
return getCertificatesFromPersistent(institutionId)
|
||||
|
@ -81,8 +95,19 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
final String alias,
|
||||
final Certificate certificate) {
|
||||
|
||||
return this.addCertificate(institutionId, alias, certificate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<CertificateInfo> addCertificate(
|
||||
final Long institutionId,
|
||||
final String alias,
|
||||
final Certificate certificate,
|
||||
final PrivateKey privateKey) {
|
||||
|
||||
return getCertificatesFromPersistent(institutionId)
|
||||
.flatMap(record -> addCertificate(record, alias, certificate))
|
||||
.flatMap(record -> addCertificate(record, alias, certificate, privateKey))
|
||||
.flatMap(this::storeUpdate)
|
||||
.flatMap(certs -> CertificateDAO.getDataFromCertificate(certs, alias))
|
||||
.onError(TransactionHandler::rollback);
|
||||
|
@ -99,6 +124,18 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<String>> getAllIdentityAlias(final Long institutionId) {
|
||||
return getCertificates(institutionId)
|
||||
.map(certs -> certs.aliases
|
||||
.stream()
|
||||
.filter(alias -> this.cryptor
|
||||
.getPrivateKey(certs.keyStore, alias)
|
||||
.hasValue())
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private Certificates createNewCertificateStore(final Long institutionId) {
|
||||
return this.cryptor.createNewEmptyKeyStore()
|
||||
.map(store -> new CertificateRecord(
|
||||
|
@ -121,12 +158,13 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
private Result<Certificates> addCertificate(
|
||||
final CertificateRecord record,
|
||||
final String alias,
|
||||
final Certificate certificate) {
|
||||
final Certificate certificate,
|
||||
final PrivateKey privateKey) {
|
||||
|
||||
return loadCertificateStore(record.getCertStore())
|
||||
.map(store -> checkPresent(alias, store, certificate))
|
||||
.map(store -> {
|
||||
addToStore(alias, certificate, store);
|
||||
addToStore(alias, certificate, privateKey, store);
|
||||
return new Certificates(
|
||||
record.getId(),
|
||||
record.getInstitutionId(),
|
||||
|
@ -138,10 +176,18 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
private void addToStore(
|
||||
final String alias,
|
||||
final Certificate certificate,
|
||||
final PrivateKey privateKey,
|
||||
final PKCS12KeyStoreSpi store) {
|
||||
|
||||
try {
|
||||
// Add the certificate to the key store
|
||||
store.engineSetCertificateEntry(alias, certificate);
|
||||
// Add the private key to the key store with internal password protection
|
||||
if (privateKey != null) {
|
||||
this.cryptor.addPrivateKey(store, privateKey, alias, certificate)
|
||||
.onError(error -> log.error("Failed to add private key for certificate: {}", alias, error));
|
||||
}
|
||||
|
||||
} catch (final KeyStoreException e) {
|
||||
throw new RuntimeException("Failed to add certificate to keystore. Cause: ", e);
|
||||
}
|
||||
|
@ -188,7 +234,7 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
return new Certificates(
|
||||
record.getId(),
|
||||
record.getInstitutionId(),
|
||||
joinAliases(record.getAliases(), alias),
|
||||
removeAlias(record.getAliases(), alias),
|
||||
store);
|
||||
});
|
||||
}
|
||||
|
@ -203,6 +249,12 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
}
|
||||
}
|
||||
|
||||
private Collection<String> removeAlias(final String aliases, final String alias) {
|
||||
final Collection<String> listFromString = new ArrayList<>(Utils.getListFromString(aliases));
|
||||
listFromString.remove(alias);
|
||||
return listFromString;
|
||||
}
|
||||
|
||||
private Result<Certificates> storeUpdate(final Certificates certificates) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
|
|
@ -546,6 +546,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
|||
|
||||
final CharSequence encrypted_encrypt_secret = examConfigurationMap.hasEncryptionSecret()
|
||||
? this.clientCredentialService.encrypt(examConfigurationMap.encryptSecret)
|
||||
.getOrThrow()
|
||||
: null;
|
||||
return (encrypted_encrypt_secret != null) ? encrypted_encrypt_secret.toString() : null;
|
||||
}
|
||||
|
|
|
@ -349,7 +349,6 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
record.getLmsProxyAuthUsername(),
|
||||
record.getLmsProxyAuthSecret());
|
||||
|
||||
final CharSequence plainAccessToken = this.clientCredentialService.getPlainAccessToken(clientCredentials);
|
||||
return Result.tryCatch(() -> new LmsSetup(
|
||||
record.getId(),
|
||||
record.getInstitutionId(),
|
||||
|
@ -358,7 +357,10 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
Utils.toString(clientCredentials.clientId),
|
||||
null,
|
||||
record.getLmsUrl(),
|
||||
Utils.toString(plainAccessToken),
|
||||
Utils.toString(
|
||||
this.clientCredentialService
|
||||
.getPlainAccessToken(clientCredentials)
|
||||
.getOr(null)),
|
||||
record.getLmsProxyHost(),
|
||||
record.getLmsProxyPort(),
|
||||
Utils.toString(proxyCredentials.clientId),
|
||||
|
@ -390,14 +392,16 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
? new ClientCredentials(null, null)
|
||||
: this.clientCredentialService.encryptClientCredentials(
|
||||
lmsSetup.proxyAuthUsername,
|
||||
lmsSetup.proxyAuthSecret);
|
||||
lmsSetup.proxyAuthSecret)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
private ClientCredentials createAPIClientCredentials(final LmsSetup lmsSetup) {
|
||||
return this.clientCredentialService.encryptClientCredentials(
|
||||
lmsSetup.lmsAuthName,
|
||||
lmsSetup.lmsAuthSecret,
|
||||
lmsSetup.lmsRestApiToken);
|
||||
lmsSetup.lmsRestApiToken)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -428,6 +428,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO {
|
|||
record.getDate(),
|
||||
record.getEncryptSecret(),
|
||||
null,
|
||||
additionalAttributes.containsKey(SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ALIAS)
|
||||
? additionalAttributes.get(SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ALIAS).getValue()
|
||||
: null,
|
||||
BooleanUtils.toBooleanObject(record.getActive())));
|
||||
}
|
||||
|
||||
|
@ -438,7 +441,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO {
|
|||
}
|
||||
|
||||
final CharSequence encrypted_encrypt_secret = sebClientConfig.hasEncryptionSecret()
|
||||
? this.clientCredentialService.encrypt(sebClientConfig.encryptSecret)
|
||||
? this.clientCredentialService
|
||||
.encrypt(sebClientConfig.encryptSecret)
|
||||
.getOrThrow()
|
||||
: null;
|
||||
return (encrypted_encrypt_secret != null) ? encrypted_encrypt_secret.toString() : null;
|
||||
}
|
||||
|
@ -590,6 +595,19 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO {
|
|||
configId,
|
||||
SEBClientConfig.ATTR_QUIT_PASSWORD);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(sebClientConfig.encryptCertificateAlias)) {
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.SEB_CLIENT_CONFIGURATION,
|
||||
configId,
|
||||
SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ALIAS,
|
||||
sebClientConfig.encryptCertificateAlias);
|
||||
} else {
|
||||
this.additionalAttributesDAO.delete(
|
||||
EntityType.SEB_CLIENT_CONFIGURATION,
|
||||
configId,
|
||||
SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ALIAS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -132,7 +132,8 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
final ClientCredentials lmsCredentials = this.clientCredentialService.encryptClientCredentials(
|
||||
lmsSetup.lmsAuthName,
|
||||
lmsSetup.lmsAuthSecret,
|
||||
lmsSetup.lmsRestApiToken);
|
||||
lmsSetup.lmsRestApiToken)
|
||||
.getOrThrow();
|
||||
|
||||
final ProxyData proxyData = (StringUtils.isNoneBlank(lmsSetup.proxyHost))
|
||||
? new ProxyData(
|
||||
|
@ -140,7 +141,8 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
lmsSetup.proxyPort,
|
||||
this.clientCredentialService.encryptClientCredentials(
|
||||
lmsSetup.proxyAuthUsername,
|
||||
lmsSetup.proxyAuthSecret))
|
||||
lmsSetup.proxyAuthSecret)
|
||||
.getOrThrow())
|
||||
: null;
|
||||
|
||||
return test(createLmsSetupTemplate(lmsSetup, lmsCredentials, proxyData));
|
||||
|
|
|
@ -140,7 +140,9 @@ final class OpenEdxRestTemplateFactory {
|
|||
final String accessTokenRequestPath) throws URISyntaxException {
|
||||
|
||||
final CharSequence plainClientId = credentials.clientId;
|
||||
final CharSequence plainClientSecret = this.clientCredentialService.getPlainClientSecret(credentials);
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(credentials)
|
||||
.getOrThrow();
|
||||
|
||||
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
||||
details.setAccessTokenUri(lmsSetup.lmsApiUrl + accessTokenRequestPath);
|
||||
|
|
|
@ -164,7 +164,9 @@ class MoodleRestTemplateFactory {
|
|||
final String accessTokenRequestPath) {
|
||||
|
||||
final CharSequence plainClientId = credentials.clientId;
|
||||
final CharSequence plainClientSecret = this.clientCredentialService.getPlainClientSecret(credentials);
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(credentials)
|
||||
.getOrThrow();
|
||||
|
||||
final MoodleAPIRestTemplate restTemplate = new MoodleAPIRestTemplate(
|
||||
this.jsonMapper,
|
||||
|
|
|
@ -34,6 +34,7 @@ public interface CertificateService {
|
|||
Long institutionId,
|
||||
CertificateFileType certificateFileType,
|
||||
String alias,
|
||||
CharSequence password,
|
||||
InputStream in);
|
||||
|
||||
Result<EntityKey> removeCertificate(Long institutionId, String alias);
|
||||
|
|
|
@ -29,9 +29,8 @@ public interface SEBConfigEncryptionContext {
|
|||
|
||||
/** Get a defined Certificate if supported.
|
||||
*
|
||||
* @param key The key of the Certificate to get
|
||||
* @return a defined Certificate
|
||||
* @throws UnsupportedOperationException if not supported */
|
||||
Certificate getCertificate(CharSequence key);
|
||||
Certificate getCertificate();
|
||||
|
||||
}
|
||||
|
|
|
@ -38,12 +38,11 @@ public interface SEBConfigEncryptionService {
|
|||
PASSWORD_PSWD(Type.PASSWORD, "pswd"),
|
||||
/** Password encryption with 'pwcc' header */
|
||||
PASSWORD_PWCC(Type.PASSWORD, "pwcc"),
|
||||
|
||||
// NOTE not supported yet but eventually needed for SEB config import.
|
||||
// PUBLIC_KEY_HASH(Type.CERTIFICATE, "pkhs"),
|
||||
// PUBLIC_KEY_HASH_SYMMETRIC_KEY(Type.CERTIFICATE, "phsk")
|
||||
|
||||
;
|
||||
/** Encryption with public/private asymmetric keys and symmetric key */
|
||||
PUBLIC_KEY_HASH_SYMMETRIC_KEY(Type.CERTIFICATE, "phsk"),
|
||||
// NOTE not supported yet but eventually needed for SEB config import.
|
||||
/** Encryption with public/private key */
|
||||
PUBLIC_KEY_HASH(Type.CERTIFICATE, "pkhs");
|
||||
|
||||
public final Type type;
|
||||
public final byte[] header;
|
||||
|
|
|
@ -19,11 +19,11 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
import org.cryptonode.jncryptor.AES256JNCryptor;
|
||||
import org.cryptonode.jncryptor.CryptorException;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
|
||||
class AES256JNCryptorEmptyPwdSupport extends AES256JNCryptor {
|
||||
|
||||
static final String KEY_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
|
||||
static final int AES_256_KEY_SIZE = 256 / 8;
|
||||
static final String AES_NAME = "AES";
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
protected AES256JNCryptorEmptyPwdSupport() {
|
||||
|
@ -44,14 +44,14 @@ class AES256JNCryptorEmptyPwdSupport extends AES256JNCryptor {
|
|||
public SecretKey keyForPassword(final char[] password, final byte[] salt) throws CryptorException {
|
||||
try {
|
||||
final SecretKeyFactory factory = SecretKeyFactory
|
||||
.getInstance(KEY_DERIVATION_ALGORITHM);
|
||||
.getInstance(Constants.KEY_DERIVATION_ALGORITHM);
|
||||
final SecretKey tmp = factory.generateSecret(new PBEKeySpec(password, salt,
|
||||
getPBKDFIterations(), AES_256_KEY_SIZE * 8));
|
||||
return new SecretKeySpec(tmp.getEncoded(), AES_NAME);
|
||||
return new SecretKeySpec(tmp.getEncoded(), Constants.AES);
|
||||
} catch (final GeneralSecurityException e) {
|
||||
throw new CryptorException(String.format(
|
||||
"Failed to generate key from password using %s.",
|
||||
KEY_DERIVATION_ALGORITHM), e);
|
||||
Constants.KEY_DERIVATION_ALGORITHM), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ import javax.crypto.spec.IvParameterSpec;
|
|||
import org.apache.commons.lang3.Validate;
|
||||
import org.cryptonode.jncryptor.CryptorException;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
|
||||
class AES256JNCryptorOutputStreamEmptyPwdSupport extends OutputStream {
|
||||
|
||||
static final int SALT_LENGTH = 8;
|
||||
static final int AES_BLOCK_SIZE = 16;
|
||||
static final String AES_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
|
||||
static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||
static final int VERSION = 3;
|
||||
static final int FLAG_PASSWORD = 0x01;
|
||||
|
||||
|
@ -86,11 +86,11 @@ class AES256JNCryptorOutputStreamEmptyPwdSupport extends OutputStream {
|
|||
this.iv = iv;
|
||||
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
|
||||
final Cipher cipher = Cipher.getInstance(Constants.AES_CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
|
||||
|
||||
try {
|
||||
final Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
||||
final Mac mac = Mac.getInstance(Constants.HMAC_ALGORITHM);
|
||||
mac.init(hmacKey);
|
||||
|
||||
this.macOutputStream = new MacOutputStream(out, mac);
|
||||
|
|
|
@ -9,24 +9,30 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo.CertificateFileType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Certificates;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Pair;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.CertificateDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.CertificateService;
|
||||
|
@ -72,13 +78,27 @@ public class CertificateServiceImpl implements CertificateService {
|
|||
final Long institutionId,
|
||||
final CertificateFileType certificateFileType,
|
||||
final String alias,
|
||||
final CharSequence password,
|
||||
final InputStream in) {
|
||||
|
||||
return loadCertFromInput(institutionId, certificateFileType, in)
|
||||
.flatMap(cert -> this.certificateDAO.addCertificate(
|
||||
institutionId,
|
||||
CertificateDAO.extractAlias(cert, alias),
|
||||
cert));
|
||||
switch (certificateFileType) {
|
||||
case PEM:
|
||||
return loadCertFromPEM(in)
|
||||
.flatMap(cert -> this.certificateDAO.addCertificate(
|
||||
institutionId,
|
||||
CertificateDAO.extractAlias(cert, alias),
|
||||
cert));
|
||||
|
||||
case PKCS12:
|
||||
return loadCertFromPKC(in, password)
|
||||
.flatMap(pair -> this.certificateDAO.addCertificate(
|
||||
institutionId,
|
||||
CertificateDAO.extractAlias(pair.a, alias),
|
||||
pair.a,
|
||||
pair.b));
|
||||
default:
|
||||
return Result.ofRuntimeError("Unsupported certificate type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,26 +143,26 @@ public class CertificateServiceImpl implements CertificateService {
|
|||
return getDataFromCertificates(certificates, data -> true);
|
||||
}
|
||||
|
||||
private Result<X509Certificate> loadCertFromInput(
|
||||
final Long institutionId,
|
||||
final CertificateFileType certificateFileType,
|
||||
final InputStream in) {
|
||||
|
||||
switch (certificateFileType) {
|
||||
case PEM:
|
||||
return loadCertFromPEM(institutionId, in);
|
||||
case PKCS12:
|
||||
return Result.ofRuntimeError("Not supported yet");
|
||||
default:
|
||||
return Result.ofRuntimeError("Unsupported certificate type");
|
||||
}
|
||||
}
|
||||
|
||||
private Result<X509Certificate> loadCertFromPEM(final Long institutionId, final InputStream in) {
|
||||
private Result<X509Certificate> loadCertFromPEM(final InputStream in) {
|
||||
return Result.tryCatch(() -> {
|
||||
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
final CertificateFactory certFactory = CertificateFactory.getInstance(Constants.X_509);
|
||||
return (X509Certificate) certFactory.generateCertificate(in);
|
||||
});
|
||||
}
|
||||
|
||||
private Result<Pair<X509Certificate, PrivateKey>> loadCertFromPKC(
|
||||
final InputStream in,
|
||||
final CharSequence password) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final KeyStore ks = KeyStore.getInstance(Constants.PKCS_12);
|
||||
ks.load(in, Utils.toCharArray(password));
|
||||
final Enumeration<String> aliases = ks.aliases();
|
||||
final String alias = aliases.nextElement();
|
||||
final X509Certificate certificate = (X509Certificate) ks.getCertificate(alias);
|
||||
final PrivateKey pKey = (PrivateKey) ks.getKey(alias, Utils.toCharArray(password));
|
||||
return new Pair<>(certificate, pKey);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Base64;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
|
||||
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigCryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class CertificateSymetricKeyCryptor implements SEBConfigCryptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CertificateSymetricKeyCryptor.class);
|
||||
|
||||
private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf(
|
||||
Strategy.PUBLIC_KEY_HASH_SYMMETRIC_KEY);
|
||||
private static final int PUBLIC_KEY_HASH_SIZE = 20;
|
||||
private static final int ENCRYPTION_KEY_LENGTH = 32;
|
||||
private static final int KEY_LENGTH_SIZE = 4;
|
||||
|
||||
private final PasswordEncryptor passwordEncryptor;
|
||||
private final PasswordDecryptor passwordDecryptor;
|
||||
|
||||
public CertificateSymetricKeyCryptor(
|
||||
final PasswordEncryptor passwordEncryptor,
|
||||
final PasswordDecryptor passwordDecryptor) {
|
||||
|
||||
this.passwordEncryptor = passwordEncryptor;
|
||||
this.passwordDecryptor = passwordDecryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Strategy> strategies() {
|
||||
return STRATEGIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous certificate encryption");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final Certificate certificate = context.getCertificate();
|
||||
final byte[] publicKeyHash = generatePublicKeyHash(certificate);
|
||||
final byte[] symetricKey = generateSymetricKey();
|
||||
final CharSequence symetricKeyBase64 = Base64.getEncoder().encodeToString(symetricKey);
|
||||
final byte[] generateParameter = generateParameter(certificate, publicKeyHash, symetricKey);
|
||||
|
||||
output.write(generateParameter, 0, generateParameter.length);
|
||||
this.passwordEncryptor.encrypt(output, input, symetricKeyBase64);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Error while trying to stream and encrypt data: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
try {
|
||||
final Certificate certificate = context.getCertificate();
|
||||
final byte[] publicKeyHash = parsePublicKeyHash(input);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Error while trying to stream and decrypt data: ", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private byte[] generatePublicKeyHash(final Certificate cert) throws NoSuchAlgorithmException {
|
||||
final byte[] publicKey = cert.getPublicKey().getEncoded();
|
||||
final MessageDigest md = MessageDigest.getInstance(Constants.SHA_1);
|
||||
final byte[] hash = md.digest(publicKey);
|
||||
return hash;
|
||||
}
|
||||
|
||||
private byte[] generateSymetricKey() throws NoSuchAlgorithmException {
|
||||
final KeyGenerator keyGenerator = KeyGenerator.getInstance(Constants.AES);
|
||||
keyGenerator.init(ENCRYPTION_KEY_LENGTH);
|
||||
return keyGenerator.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private byte[] generateParameter(
|
||||
final Certificate cert,
|
||||
final byte[] publicKeyHash,
|
||||
final byte[] symetricKey) throws Exception {
|
||||
|
||||
final ByteArrayOutputStream data = new ByteArrayOutputStream();
|
||||
final byte[] encryptedKey = generateEncryptedKey(cert, symetricKey);
|
||||
final byte[] encryptedKeyLength = ByteBuffer
|
||||
.allocate(KEY_LENGTH_SIZE)
|
||||
.putInt(encryptedKey.length)
|
||||
.array();
|
||||
|
||||
data.write(publicKeyHash, 0, publicKeyHash.length);
|
||||
data.write(encryptedKeyLength, 0, encryptedKeyLength.length);
|
||||
data.write(encryptedKey, 0, encryptedKey.length);
|
||||
|
||||
return data.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] generateEncryptedKey(final Certificate cert, final byte[] symetricKey) throws Exception {
|
||||
final String algorithm = cert.getPublicKey().getAlgorithm();
|
||||
final Cipher encryptCipher = Cipher.getInstance(algorithm);
|
||||
encryptCipher.init(Cipher.ENCRYPT_MODE, cert);
|
||||
final byte[] cipherText = encryptCipher.doFinal(symetricKey);
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
private byte[] parsePublicKeyHash(final InputStream input) throws IOException {
|
||||
final byte[] publicKeyHash = new byte[PUBLIC_KEY_HASH_SIZE];
|
||||
input.read(publicKeyHash, 0, publicKeyHash.length);
|
||||
return publicKeyHash;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
|
@ -15,6 +16,7 @@ import java.io.PipedOutputStream;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -54,10 +56,11 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.CertificateDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SEBConfigEncryptionServiceImpl.EncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration;
|
||||
|
@ -165,6 +168,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
private final PasswordEncoder clientPasswordEncoder;
|
||||
private final ZipService zipService;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
private final CertificateDAO certificateDAO;
|
||||
private final long defaultPingInterval;
|
||||
|
||||
protected ClientConfigServiceImpl(
|
||||
|
@ -172,8 +176,9 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
final ClientCredentialService clientCredentialService,
|
||||
final SEBConfigEncryptionService sebConfigEncryptionService,
|
||||
final ZipService zipService,
|
||||
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final CertificateDAO certificateDAO,
|
||||
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder,
|
||||
@Value("${sebserver.webservice.api.exam.defaultPingInterval:1000}") final long defaultPingInterval) {
|
||||
|
||||
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||
|
@ -182,6 +187,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
this.zipService = zipService;
|
||||
this.clientPasswordEncoder = clientPasswordEncoder;
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.certificateDAO = certificateDAO;
|
||||
this.defaultPingInterval = defaultPingInterval;
|
||||
}
|
||||
|
||||
|
@ -224,18 +230,13 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
final SEBClientConfig config = this.sebClientConfigDAO
|
||||
.byModelId(modelId).getOrThrow();
|
||||
|
||||
final CharSequence encryptionPassword = this.sebClientConfigDAO
|
||||
.getConfigPasswordCipher(config.getModelId())
|
||||
.getOr((config.getConfigPurpose() == ConfigPurpose.START_EXAM) ? null : StringUtils.EMPTY);
|
||||
|
||||
exportSEBClientConfiguration(output, examId, config, encryptionPassword);
|
||||
exportSEBClientConfiguration(output, examId, config);
|
||||
}
|
||||
|
||||
protected void exportSEBClientConfiguration(
|
||||
final OutputStream output,
|
||||
final Long examId,
|
||||
final SEBClientConfig config,
|
||||
final CharSequence encryptionPassword) {
|
||||
final SEBClientConfig config) {
|
||||
|
||||
final String plainTextXMLContent = extractXMLContent(config, examId);
|
||||
|
||||
|
@ -263,9 +264,10 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
// ZIP plain text
|
||||
this.zipService.write(pOut, plainIn);
|
||||
|
||||
if (encryptionPassword != null) {
|
||||
// encrypt zipped plain text and add header
|
||||
passwordEncryption(zipOut, encryptionPassword, config.getConfigPurpose(), pIn);
|
||||
if (StringUtils.isNotBlank(config.encryptCertificateAlias)) {
|
||||
certificateEncryption(zipOut, config, pIn);
|
||||
} else if (config.hasEncryptionSecret()) {
|
||||
passwordEncryption(zipOut, config, pIn);
|
||||
} else {
|
||||
// just add plain text header
|
||||
this.sebConfigEncryptionService.streamEncrypted(
|
||||
|
@ -290,6 +292,34 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
}
|
||||
}
|
||||
|
||||
private SEBConfigEncryptionContext buildCertificateEncryptionContext(final SEBClientConfig config) {
|
||||
|
||||
final Certificate certificate = this.certificateDAO.getCertificate(
|
||||
config.institutionId,
|
||||
String.valueOf(config.getEncryptCertificateAlias()))
|
||||
.getOrThrow();
|
||||
|
||||
return EncryptionContext.contextOf(
|
||||
SEBConfigEncryptionService.Strategy.PUBLIC_KEY_HASH_SYMMETRIC_KEY,
|
||||
certificate);
|
||||
}
|
||||
|
||||
private SEBConfigEncryptionContext buildPasswordEncryptionContext(final SEBClientConfig config) {
|
||||
final CharSequence encryptionPassword = this.sebClientConfigDAO
|
||||
.getConfigPasswordCipher(config.getModelId())
|
||||
.getOr((config.getConfigPurpose() == ConfigPurpose.START_EXAM) ? null : StringUtils.EMPTY);
|
||||
final CharSequence plainTextPassword = (StringUtils.isNotBlank(encryptionPassword))
|
||||
? getPlainTextPassword(
|
||||
encryptionPassword,
|
||||
config.configPurpose)
|
||||
: null;
|
||||
return EncryptionContext.contextOf(
|
||||
(config.configPurpose == ConfigPurpose.CONFIGURE_CLIENT)
|
||||
? SEBConfigEncryptionService.Strategy.PASSWORD_PWCC
|
||||
: SEBConfigEncryptionService.Strategy.PASSWORD_PSWD,
|
||||
plainTextPassword);
|
||||
}
|
||||
|
||||
private String extractXMLContent(final SEBClientConfig config, final Long examId) {
|
||||
|
||||
final String fallbackAddition = getFallbackAddition(config);
|
||||
|
@ -309,7 +339,8 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
.getOrThrow();
|
||||
final CharSequence plainClientId = sebClientCredentials.clientId;
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(sebClientCredentials);
|
||||
.getPlainClientSecret(sebClientCredentials)
|
||||
.getOrThrow();
|
||||
|
||||
final String plainTextConfig = String.format(
|
||||
SEB_CLIENT_CONFIG_TEMPLATE_XML,
|
||||
|
@ -389,7 +420,9 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
config.fallbackAttemptInterval);
|
||||
|
||||
if (StringUtils.isNotBlank(config.fallbackPassword)) {
|
||||
final CharSequence decrypt = this.clientCredentialService.decrypt(config.fallbackPassword);
|
||||
final CharSequence decrypt = this.clientCredentialService
|
||||
.decrypt(config.fallbackPassword)
|
||||
.getOrThrow();
|
||||
fallbackAddition += String.format(
|
||||
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
|
||||
SEBClientConfig.ATTR_FALLBACK_PASSWORD,
|
||||
|
@ -397,7 +430,9 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
}
|
||||
|
||||
if (StringUtils.isNotBlank(config.quitPassword)) {
|
||||
final CharSequence decrypt = this.clientCredentialService.decrypt(config.quitPassword);
|
||||
final CharSequence decrypt = this.clientCredentialService
|
||||
.decrypt(config.quitPassword)
|
||||
.getOrThrow();
|
||||
fallbackAddition += String.format(
|
||||
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
|
||||
SEBClientConfig.ATTR_QUIT_PASSWORD,
|
||||
|
@ -428,7 +463,9 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
final ClientCredentials credentials = this.sebClientConfigDAO
|
||||
.getSEBClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
final CharSequence plainClientSecret = this.clientCredentialService.getPlainClientSecret(credentials);
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(credentials)
|
||||
.getOrThrow();
|
||||
final String basicAuth = credentials.clientId +
|
||||
String.valueOf(Constants.COLON) +
|
||||
plainClientSecret;
|
||||
|
@ -463,28 +500,46 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
checkAccess(config);
|
||||
}
|
||||
|
||||
private void certificateEncryption(
|
||||
final OutputStream out,
|
||||
final SEBClientConfig config,
|
||||
final InputStream in) throws IOException {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** SEB client configuration with certificate based encryption");
|
||||
}
|
||||
|
||||
final boolean withPasswordEncryption = config.hasEncryptionSecret();
|
||||
|
||||
PipedOutputStream passEncryptionOut = null;
|
||||
PipedInputStream passEncryptionIn = null;
|
||||
|
||||
if (withPasswordEncryption) {
|
||||
// encrypt with password first
|
||||
passEncryptionOut = new PipedOutputStream();
|
||||
passEncryptionIn = new PipedInputStream(passEncryptionOut);
|
||||
passwordEncryption(passEncryptionOut, config, in);
|
||||
}
|
||||
|
||||
this.sebConfigEncryptionService.streamEncrypted(
|
||||
out,
|
||||
(withPasswordEncryption) ? in : passEncryptionIn,
|
||||
buildCertificateEncryptionContext(config));
|
||||
}
|
||||
|
||||
private void passwordEncryption(
|
||||
final OutputStream output,
|
||||
final CharSequence encryptionPassword,
|
||||
final ConfigPurpose configPurpose,
|
||||
final SEBClientConfig config,
|
||||
final InputStream input) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** SEB client configuration with password based encryption");
|
||||
}
|
||||
|
||||
final CharSequence plainTextPassword = getPlainTextPassword(
|
||||
encryptionPassword,
|
||||
configPurpose);
|
||||
|
||||
this.sebConfigEncryptionService.streamEncrypted(
|
||||
output,
|
||||
input,
|
||||
EncryptionContext.contextOf(
|
||||
(configPurpose == ConfigPurpose.CONFIGURE_CLIENT)
|
||||
? Strategy.PASSWORD_PWCC
|
||||
: Strategy.PASSWORD_PSWD,
|
||||
plainTextPassword));
|
||||
buildPasswordEncryptionContext(config));
|
||||
}
|
||||
|
||||
private CharSequence getPlainTextPassword(
|
||||
|
@ -493,7 +548,9 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
|
||||
CharSequence plainTextPassword = (encryptionPassword == StringUtils.EMPTY)
|
||||
? StringUtils.EMPTY
|
||||
: this.clientCredentialService.decrypt(encryptionPassword);
|
||||
: this.clientCredentialService
|
||||
.decrypt(encryptionPassword)
|
||||
.getOrThrow();
|
||||
|
||||
if (configPurpose == ConfigPurpose.CONFIGURE_CLIENT && plainTextPassword != StringUtils.EMPTY) {
|
||||
MessageDigest digest;
|
||||
|
@ -518,7 +575,10 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
* @return encoded clientSecret for that SEBClientConfiguration with clientId or null of not existing */
|
||||
private Result<CharSequence> getEncodedClientConfigSecret(final String clientId) {
|
||||
return this.sebClientConfigDAO.getConfigPasswordCipherByClientName(clientId)
|
||||
.map(cipher -> this.clientPasswordEncoder.encode(this.clientCredentialService.decrypt(cipher)));
|
||||
.map(cipher -> this.clientPasswordEncoder
|
||||
.encode(this.clientCredentialService
|
||||
.decrypt(cipher)
|
||||
.getOrThrow()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -188,7 +188,8 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
|||
}
|
||||
|
||||
final CharSequence encryptionPasswordPlaintext = this.clientCredentialService
|
||||
.decrypt(passwordCipher);
|
||||
.decrypt(passwordCipher)
|
||||
.getOrThrow();
|
||||
|
||||
PipedOutputStream plainOut = null;
|
||||
PipedInputStream zipIn = null;
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigCryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class PasswordCryptor implements SEBConfigCryptor {
|
||||
|
||||
private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf(
|
||||
Strategy.PASSWORD_PSWD,
|
||||
Strategy.PASSWORD_PWCC);
|
||||
|
||||
private final PasswordEncryptor passwordEncryptor;
|
||||
private final PasswordDecryptor passwordDecryptor;
|
||||
|
||||
public PasswordCryptor(final PasswordEncryptor passwordEncryptor, final PasswordDecryptor passwordDecryptor) {
|
||||
this.passwordEncryptor = passwordEncryptor;
|
||||
this.passwordDecryptor = passwordDecryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Strategy> strategies() {
|
||||
return STRATEGIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
|
||||
this.passwordEncryptor.encrypt(output, input, context.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
|
||||
this.passwordDecryptor.decrypt(output, input, context.getPassword());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptorInputStream;
|
||||
import org.cryptonode.jncryptor.CryptorException;
|
||||
import org.cryptonode.jncryptor.JNCryptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class PasswordDecryptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PasswordDecryptor.class);
|
||||
|
||||
private final JNCryptor cryptor;
|
||||
|
||||
public PasswordDecryptor(final JNCryptor cryptor) {
|
||||
this.cryptor = cryptor;
|
||||
}
|
||||
|
||||
void decrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final CharSequence password) {
|
||||
|
||||
try {
|
||||
final byte[] version = new byte[Constants.JN_CRYPTOR_VERSION_HEADER_SIZE];
|
||||
final int read = input.read(version);
|
||||
if (read != Constants.JN_CRYPTOR_VERSION_HEADER_SIZE) {
|
||||
throw new IllegalArgumentException("Failed to verify RNCrypt version from input stream file header.");
|
||||
}
|
||||
|
||||
final SequenceInputStream sequenceInputStream = new SequenceInputStream(
|
||||
new ByteArrayInputStream(version),
|
||||
input);
|
||||
|
||||
if (version[0] == 3) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous password decryption");
|
||||
}
|
||||
|
||||
AES256JNCryptorInputStream encryptInput = null;
|
||||
try {
|
||||
|
||||
encryptInput = new AES256JNCryptorInputStream(
|
||||
sequenceInputStream,
|
||||
Utils.toCharArray(password));
|
||||
|
||||
IOUtils.copyLarge(encryptInput, output);
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while trying to read/write form/to streams: ", e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(encryptInput);
|
||||
}
|
||||
} else {
|
||||
// AES256JNCryptorInputStream supports only decryption of AES256 version 3 encrypted data
|
||||
// Workaround: stop streaming and use AES256JNCryptor which supports both, version 2 and 3
|
||||
log.info("Trying to decrypt with AES256JNCryptor by load all data into memory...");
|
||||
|
||||
try {
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
IOUtils.copy(sequenceInputStream, out);
|
||||
final byte[] ciphertext = out.toByteArray();
|
||||
|
||||
final byte[] decryptData = this.cryptor.decryptData(ciphertext, Utils.toCharArray(password));
|
||||
final ByteArrayInputStream decryptedIn = new ByteArrayInputStream(decryptData);
|
||||
IOUtils.copyLarge(decryptedIn, output);
|
||||
|
||||
} catch (final IOException | CryptorException e) {
|
||||
log.error("Error while trying to none-streaming decrypt: ", e);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
log.error("Unexpected error while decryption: ", e);
|
||||
} finally {
|
||||
try {
|
||||
output.flush();
|
||||
output.close();
|
||||
} catch (final IOException e) {
|
||||
log.error("Failed to close streams");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Finish streaming asynchronous decryption");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -8,19 +8,13 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptorInputStream;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptorOutputStream;
|
||||
import org.cryptonode.jncryptor.CryptorException;
|
||||
import org.cryptonode.jncryptor.JNCryptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -29,47 +23,26 @@ import org.springframework.stereotype.Component;
|
|||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigCryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class PasswordEncryptor implements SEBConfigCryptor {
|
||||
public class PasswordEncryptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PasswordEncryptor.class);
|
||||
|
||||
private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf(
|
||||
Strategy.PASSWORD_PSWD,
|
||||
Strategy.PASSWORD_PWCC);
|
||||
|
||||
private final JNCryptor cryptor;
|
||||
|
||||
protected PasswordEncryptor(final JNCryptor cryptor) {
|
||||
this.cryptor = cryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Strategy> strategies() {
|
||||
return STRATEGIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
final CharSequence password) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous encryption");
|
||||
log.debug("*** Start streaming asynchronous password encryption");
|
||||
}
|
||||
|
||||
OutputStream encryptOutput = null;
|
||||
try {
|
||||
|
||||
final CharSequence password = context.getPassword();
|
||||
|
||||
if (password.length() == 0) {
|
||||
encryptOutput = new AES256JNCryptorOutputStreamEmptyPwdSupport(
|
||||
output,
|
||||
|
@ -105,79 +78,4 @@ public class PasswordEncryptor implements SEBConfigCryptor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SEBConfigEncryptionContext context) {
|
||||
|
||||
final CharSequence password = context.getPassword();
|
||||
|
||||
try {
|
||||
final byte[] version = new byte[Constants.JN_CRYPTOR_VERSION_HEADER_SIZE];
|
||||
final int read = input.read(version);
|
||||
if (read != Constants.JN_CRYPTOR_VERSION_HEADER_SIZE) {
|
||||
throw new IllegalArgumentException("Failed to verify RNCrypt version from input stream file header.");
|
||||
}
|
||||
|
||||
final SequenceInputStream sequenceInputStream = new SequenceInputStream(
|
||||
new ByteArrayInputStream(version),
|
||||
input);
|
||||
|
||||
if (version[0] == 3) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous decryption");
|
||||
}
|
||||
|
||||
AES256JNCryptorInputStream encryptInput = null;
|
||||
try {
|
||||
|
||||
encryptInput = new AES256JNCryptorInputStream(
|
||||
sequenceInputStream,
|
||||
Utils.toCharArray(password));
|
||||
|
||||
IOUtils.copyLarge(encryptInput, output);
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while trying to read/write form/to streams: ", e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(encryptInput);
|
||||
}
|
||||
} else {
|
||||
// AES256JNCryptorInputStream supports only decryption of AES256 version 3 encrypted data
|
||||
// Workaround: stop streaming and use AES256JNCryptor which supports both, version 2 and 3
|
||||
log.info("Trying to decrypt with AES256JNCryptor by load all data into memory...");
|
||||
|
||||
try {
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
IOUtils.copy(sequenceInputStream, out);
|
||||
final byte[] ciphertext = out.toByteArray();
|
||||
|
||||
//cryptor.setPBKDFIterations(Constants.JN_CRYPTOR_ITERATIONS);
|
||||
final byte[] decryptData = this.cryptor.decryptData(ciphertext, Utils.toCharArray(password));
|
||||
final ByteArrayInputStream decryptedIn = new ByteArrayInputStream(decryptData);
|
||||
IOUtils.copyLarge(decryptedIn, output);
|
||||
|
||||
} catch (final IOException | CryptorException e) {
|
||||
log.error("Error while trying to none-streaming decrypt: ", e);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
log.error("Unexpected error while decryption: ", e);
|
||||
} finally {
|
||||
try {
|
||||
output.flush();
|
||||
output.close();
|
||||
} catch (final IOException e) {
|
||||
log.error("Failed to close streams");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Finish streaming asynchronous decryption");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -74,7 +73,7 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption
|
|||
pin = new PipedInputStream(pout);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Password encryption with strategy: {}", strategy);
|
||||
log.debug("Encryption with strategy: {}", strategy);
|
||||
}
|
||||
|
||||
output.write(strategy.header);
|
||||
|
@ -193,16 +192,16 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption
|
|||
|
||||
public final Strategy strategy;
|
||||
public final CharSequence password;
|
||||
public final Function<CharSequence, Certificate> certificateStore;
|
||||
public final Certificate certificate;
|
||||
|
||||
private EncryptionContext(
|
||||
final Strategy strategy,
|
||||
final CharSequence password,
|
||||
final Function<CharSequence, Certificate> certificateStore) {
|
||||
final Certificate certificate) {
|
||||
|
||||
this.strategy = strategy;
|
||||
this.password = password;
|
||||
this.certificateStore = certificateStore;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -216,24 +215,24 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption
|
|||
}
|
||||
|
||||
@Override
|
||||
public Certificate getCertificate(final CharSequence key) {
|
||||
if (this.certificateStore == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return this.certificateStore.apply(key);
|
||||
public Certificate getCertificate() {
|
||||
return this.certificate;
|
||||
}
|
||||
|
||||
static SEBConfigEncryptionContext contextOf(final Strategy strategy, final CharSequence password) {
|
||||
static SEBConfigEncryptionContext contextOf(
|
||||
final Strategy strategy,
|
||||
final CharSequence password) {
|
||||
|
||||
checkPasswordBased(strategy);
|
||||
return new EncryptionContext(strategy, password, null);
|
||||
}
|
||||
|
||||
static SEBConfigEncryptionContext contextOf(
|
||||
final Strategy strategy,
|
||||
final Function<CharSequence, Certificate> certificateStore) {
|
||||
final Certificate certificate) {
|
||||
|
||||
checkCertificateBased(strategy);
|
||||
return new EncryptionContext(strategy, null, certificateStore);
|
||||
return new EncryptionContext(strategy, null, certificate);
|
||||
}
|
||||
|
||||
static void checkPasswordBased(final Strategy strategy) {
|
||||
|
|
|
@ -307,7 +307,10 @@ public class JitsiProctoringService implements ExamProctoringService {
|
|||
.build()
|
||||
.getHost();
|
||||
|
||||
final CharSequence decryptedSecret = this.cryptor.decrypt(appSecret);
|
||||
final CharSequence decryptedSecret = this.cryptor
|
||||
.decrypt(appSecret)
|
||||
.getOrThrow();
|
||||
|
||||
final String token = internalCreateAccessToken(
|
||||
appKey,
|
||||
decryptedSecret,
|
||||
|
|
|
@ -159,7 +159,9 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
|
||||
final ClientCredentials credentials = new ClientCredentials(
|
||||
proctoringSettings.appKey,
|
||||
this.cryptor.encrypt(proctoringSettings.appSecret));
|
||||
this.cryptor
|
||||
.encrypt(proctoringSettings.appSecret)
|
||||
.getOrThrow());
|
||||
|
||||
final ResponseEntity<String> result = this.zoomRestTemplate
|
||||
.testServiceConnection(
|
||||
|
@ -182,7 +184,9 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
proctoringSettings.serverURL,
|
||||
proctoringSettings.collectingRoomSize,
|
||||
proctoringSettings.appKey,
|
||||
this.cryptor.encrypt(proctoringSettings.appSecret));
|
||||
this.cryptor
|
||||
.encrypt(proctoringSettings.appSecret)
|
||||
.getOrThrow());
|
||||
|
||||
disposeServiceRoomsForExam(
|
||||
proctoringSettings.examId,
|
||||
|
@ -511,7 +515,10 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
|
||||
try {
|
||||
|
||||
final CharSequence decryptedSecret = this.cryptor.decrypt(credentials.secret);
|
||||
final CharSequence decryptedSecret = this.cryptor
|
||||
.decrypt(credentials.secret)
|
||||
.getOrThrow();
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||
|
||||
|
@ -551,7 +558,9 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
try {
|
||||
final String apiKey = credentials.clientIdAsString();
|
||||
final int status = host ? 1 : 0;
|
||||
final CharSequence decryptedSecret = this.cryptor.decrypt(credentials.secret);
|
||||
final CharSequence decryptedSecret = this.cryptor
|
||||
.decrypt(credentials.secret)
|
||||
.getOrThrow();
|
||||
|
||||
final Mac hasher = Mac.getInstance("HmacSHA256");
|
||||
final String ts = Long.toString(System.currentTimeMillis() - 30000);
|
||||
|
|
|
@ -215,7 +215,12 @@ public class CertificateController {
|
|||
try {
|
||||
|
||||
inputStream = new BufferedInputStream(request.getInputStream());
|
||||
return this.certificateService.addCertificate(institutionId, certificateFileType, alias, inputStream)
|
||||
return this.certificateService.addCertificate(
|
||||
institutionId,
|
||||
certificateFileType,
|
||||
alias,
|
||||
password,
|
||||
inputStream)
|
||||
.flatMap(certData -> this.userActivityLogDAO.log(UserLogActivityType.IMPORT, certData))
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -229,7 +234,6 @@ public class CertificateController {
|
|||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.CERTIFICATE_ALIAS_VAR_PATH_SEGMENT,
|
||||
method = RequestMethod.DELETE,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
|
|
|
@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
|||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
@ -46,6 +47,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.SebClientConfigRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
|
@ -85,9 +87,15 @@ public class SEBClientConfigController extends ActivatableEntityController<SEBCl
|
|||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public void downloadSEBConfig(
|
||||
@PathVariable final String modelId,
|
||||
@RequestParam(
|
||||
name = Entity.FILTER_ATTR_INSTITUTION,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@RequestParam(name = EXAM.ATTR_ID, required = false) final Long examId,
|
||||
final HttpServletResponse response) throws IOException {
|
||||
|
||||
checkReadPrivilege(institutionId);
|
||||
|
||||
this.entityDAO.byModelId(modelId)
|
||||
.flatMap(this.authorization::checkWrite)
|
||||
.map(this.userActivityLogDAO::logExport);
|
||||
|
|
|
@ -119,7 +119,13 @@ public class ModelObjectJSONGenerator {
|
|||
VDIType.NO, null, null, null,
|
||||
true, "fallbackStartURL", 20000L, (short) 3, (short) 1000, "fallbackPassword",
|
||||
"fallbackPasswordConfirm",
|
||||
"quitPassword", "quitPasswordConfirm", DateTime.now(), "encryptSecret", "encryptSecretConfirm", true);
|
||||
"quitPassword",
|
||||
"quitPasswordConfirm",
|
||||
DateTime.now(),
|
||||
"encryptSecret",
|
||||
"encryptSecretConfirm",
|
||||
"certAlias",
|
||||
true);
|
||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
|
||||
|
||||
|
|
|
@ -21,16 +21,16 @@ public class CryptorTest {
|
|||
public void testEncryptDecrypt() {
|
||||
final String clientName = "simpleClientName";
|
||||
String encrypted =
|
||||
Cryptor.encrypt(clientName, "secret1").toString();
|
||||
String decrypted = Cryptor.decrypt(encrypted, "secret1").toString();
|
||||
Cryptor.encrypt(clientName, "secret1").getOrThrow().toString();
|
||||
String decrypted = Cryptor.decrypt(encrypted, "secret1").getOrThrow().toString();
|
||||
|
||||
assertEquals(clientName, decrypted);
|
||||
|
||||
final String clientSecret = "fbjreij39ru29305ruࣣàèLöäöäü65%(/%(ç87";
|
||||
|
||||
encrypted =
|
||||
Cryptor.encrypt(clientSecret, "secret1").toString();
|
||||
decrypted = Cryptor.decrypt(encrypted, "secret1").toString();
|
||||
Cryptor.encrypt(clientSecret, "secret1").getOrThrow().toString();
|
||||
decrypted = Cryptor.decrypt(encrypted, "secret1").getOrThrow().toString();
|
||||
|
||||
assertEquals(clientSecret, decrypted);
|
||||
}
|
||||
|
@ -45,16 +45,16 @@ public class CryptorTest {
|
|||
final String clientName = "simpleClientName";
|
||||
|
||||
String encrypted =
|
||||
cryptor.encrypt(clientName).toString();
|
||||
String decrypted = cryptor.decrypt(encrypted).toString();
|
||||
cryptor.encrypt(clientName).getOrThrow().toString();
|
||||
String decrypted = cryptor.decrypt(encrypted).getOrThrow().toString();
|
||||
|
||||
assertEquals(clientName, decrypted);
|
||||
|
||||
final String clientSecret = "fbjreij39ru29305ruࣣàèLöäöäü65%(/%(ç87";
|
||||
|
||||
encrypted =
|
||||
cryptor.encrypt(clientSecret).toString();
|
||||
decrypted = cryptor.decrypt(encrypted).toString();
|
||||
cryptor.encrypt(clientSecret).getOrThrow().toString();
|
||||
decrypted = cryptor.decrypt(encrypted).getOrThrow().toString();
|
||||
|
||||
assertEquals(clientSecret, decrypted);
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
|
|||
null,
|
||||
"password",
|
||||
null,
|
||||
"certAlias",
|
||||
null))
|
||||
.call();
|
||||
|
||||
|
@ -153,6 +154,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
|
|||
null,
|
||||
"password",
|
||||
"password",
|
||||
"certAlias",
|
||||
null))
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
|
|
@ -2124,7 +2124,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
|||
try {
|
||||
new SEBClientBot(
|
||||
credentials.clientIdAsString(),
|
||||
this.cryptor.decrypt(credentials.secret).toString(),
|
||||
this.cryptor.decrypt(credentials.secret).getOrThrow().toString(),
|
||||
exam.getModelId(),
|
||||
String.valueOf(exam.institutionId));
|
||||
Thread.sleep(1000);
|
||||
|
|
|
@ -65,7 +65,9 @@ public class PasswordEncryptorTest {
|
|||
@Test
|
||||
public void test2() throws IOException {
|
||||
final JNCryptor cryptor = new AES256JNCryptor();
|
||||
final PasswordEncryptor encryptor = new PasswordEncryptor(cryptor);
|
||||
final PasswordCryptor encryptor = new PasswordCryptor(
|
||||
new PasswordEncryptor(),
|
||||
new PasswordDecryptor(cryptor));
|
||||
|
||||
final String config = "<TestConfig></TestConfig>";
|
||||
final String pwd = "password";
|
||||
|
|
|
@ -99,7 +99,9 @@ public class SebConfigEncryptionServiceImplTest {
|
|||
private SEBConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl() {
|
||||
final JNCryptor cryptor = new AES256JNCryptor();
|
||||
final List<SEBConfigCryptor> encryptors = Arrays.asList(
|
||||
new PasswordEncryptor(cryptor),
|
||||
new PasswordCryptor(
|
||||
new PasswordEncryptor(),
|
||||
new PasswordDecryptor(cryptor)),
|
||||
new NoneEncryptor());
|
||||
return new SEBConfigEncryptionServiceImpl(encryptors);
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@ import org.mockito.Mockito;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public class ExamJITSIProctoringServiceTest {
|
||||
|
||||
@Test
|
||||
public void testTokenPayload() throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123"));
|
||||
final JitsiProctoringService examJITSIProctoringService =
|
||||
new JitsiProctoringService(null, null, cryptorMock, null);
|
||||
|
||||
|
@ -59,7 +60,7 @@ public class ExamJITSIProctoringServiceTest {
|
|||
@Test
|
||||
public void testCreateProctoringURL() {
|
||||
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123"));
|
||||
final JitsiProctoringService examJITSIProctoringService =
|
||||
new JitsiProctoringService(null, null, cryptorMock, null);
|
||||
final ProctoringRoomConnection data = examJITSIProctoringService.createProctoringConnection(
|
||||
|
|
Loading…
Reference in a new issue