encrypt credentials.json with AES

This commit is contained in:
Herval Freire 2017-08-04 10:51:37 -07:00
parent 84e9bd96d2
commit c3e0ead0a4
10 changed files with 149 additions and 7 deletions

View file

@ -132,6 +132,12 @@
<version>${aether.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.52</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-aether-provider</artifactId>

View file

@ -47,7 +47,21 @@ public class Credentials {
private Boolean credentialsPersist = true;
File credentialsFile;
public Credentials(Boolean credentialsPersist, String credentialsPath) {
private Encryptor encryptor;
/**
* Wrapper fro user credentials. It can load credentials from a file if credentialsPath is
* supplied, and will encrypt the file if an encryptKey is supplied.
*
* @param credentialsPersist
* @param credentialsPath
* @param encryptKey
*/
public Credentials(Boolean credentialsPersist, String credentialsPath, String encryptKey) {
if (encryptKey != null) {
this.encryptor = new Encryptor(encryptKey);
}
this.credentialsPersist = credentialsPersist;
if (credentialsPath != null) {
credentialsFile = new File(credentialsPath);
@ -119,6 +133,11 @@ public class Credentials {
fis.close();
String json = sb.toString();
if (encryptor != null) {
json = encryptor.decrypt(json);
}
CredentialsInfoSaving info = CredentialsInfoSaving.fromJson(json);
this.credentialsMap = info.credentialsMap;
} catch (IOException e) {
@ -146,6 +165,11 @@ public class Credentials {
FileOutputStream fos = new FileOutputStream(credentialsFile, false);
OutputStreamWriter out = new OutputStreamWriter(fos);
if (encryptor != null) {
jsonString = encryptor.encrypt(jsonString);
}
out.append(jsonString);
out.close();
fos.close();

View file

@ -0,0 +1,63 @@
package org.apache.zeppelin.user;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Base64;
/**
* Encrypt/decrypt arrays of bytes!
*/
public class Encryptor {
private final BufferedBlockCipher encryptCipher;
private final BufferedBlockCipher decryptCipher;
public Encryptor(String encryptKey) {
encryptCipher = new PaddedBufferedBlockCipher(new AESEngine(), new ZeroBytePadding());
encryptCipher.init(true, new KeyParameter(encryptKey.getBytes()));
decryptCipher = new PaddedBufferedBlockCipher(new AESEngine(), new ZeroBytePadding());
decryptCipher.init(false, new KeyParameter(encryptKey.getBytes()));
}
public String encrypt(String inputString) throws IOException {
byte[] input = inputString.getBytes();
byte[] result = new byte[encryptCipher.getOutputSize(input.length)];
int size = encryptCipher.processBytes(input, 0, input.length, result, 0);
try {
size += encryptCipher.doFinal(result, size);
byte[] out = new byte[size];
System.arraycopy(result, 0, out, 0, size);
return new String(Base64.encode(out));
} catch (InvalidCipherTextException e) {
throw new IOException("Cannot encrypt: " + e.getMessage(), e);
}
}
public String decrypt(String base64Input) throws IOException {
byte[] input = Base64.decode(base64Input);
byte[] result = new byte[decryptCipher.getOutputSize(input.length)];
int size = decryptCipher.processBytes(input, 0, input.length, result, 0);
try {
size += decryptCipher.doFinal(result, size);
byte[] out = new byte[size];
System.arraycopy(result, 0, out, 0, size);
return new String(out);
} catch (InvalidCipherTextException e) {
throw new IOException("Cannot decrypt: " + e.getMessage(), e);
}
}
}

View file

@ -27,7 +27,7 @@ public class CredentialsTest {
@Test
public void testDefaultProperty() throws IOException {
Credentials credentials = new Credentials(false, null);
Credentials credentials = new Credentials(false, null, null);
UserCredentials userCredentials = new UserCredentials();
UsernamePassword up1 = new UsernamePassword("user2", "password");
userCredentials.putUsernamePassword("hive(vertica)", up1);

View file

@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.user;
import java.io.IOException;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class EncryptorTest {
@Test
public void testEncryption() throws IOException {
Encryptor encryptor = new Encryptor("foobar1234567890");
String input = "test";
String encrypted = encryptor.encrypt(input);
assertNotEquals(input, encrypted);
String decrypted = encryptor.decrypt(encrypted);
assertEquals(input, decrypted);
}
}

View file

@ -132,7 +132,10 @@ public class ZeppelinServer extends Application {
this.notebookRepo = new NotebookRepoSync(conf);
this.noteSearchService = new LuceneSearch();
this.notebookAuthorization = NotebookAuthorization.init(conf);
this.credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath());
this.credentials = new Credentials(
conf.credentialsPersist(),
conf.getCredentialsPath(),
conf.getCredentialsEncryptKey());
notebook = new Notebook(conf,
notebookRepo, schedulerFactory, replFactory, interpreterSettingManager, notebookWsServer,
noteSearchService, notebookAuthorization, credentials);
@ -152,7 +155,7 @@ public class ZeppelinServer extends Application {
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
// to update notebook from application event from remote process.
heliumApplicationFactory.setNotebook(notebook);
// to update fire websocket event on application event.

View file

@ -443,6 +443,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
return getBoolean(ConfVars.ZEPPELIN_CREDENTIALS_PERSIST);
}
public String getCredentialsEncryptKey() {
return getString(ConfVars.ZEPPELIN_CREDENTIALS_ENCRYPT_KEY);
}
public String getCredentialsPath() {
return getRelativeDir(String.format("%s/credentials.json", getConfDir()));
}
@ -680,6 +684,7 @@ public class ZeppelinConfiguration extends XMLConfiguration {
ZEPPELIN_ALLOWED_ORIGINS("zeppelin.server.allowed.origins", "*"),
ZEPPELIN_ANONYMOUS_ALLOWED("zeppelin.anonymous.allowed", true),
ZEPPELIN_CREDENTIALS_PERSIST("zeppelin.credentials.persist", true),
ZEPPELIN_CREDENTIALS_ENCRYPT_KEY("zeppelin.credentials.encryptKey", null),
ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE("zeppelin.websocket.max.text.message.size", "1024000"),
ZEPPELIN_SERVER_DEFAULT_DIR_ALLOWED("zeppelin.server.default.dir.allowed", false),
ZEPPELIN_SERVER_XFRAME_OPTIONS("zeppelin.server.xframe.options", "SAMEORIGIN"),

View file

@ -77,7 +77,7 @@ public class HeliumApplicationFactoryTest extends AbstractInterpreterTest implem
this,
search,
notebookAuthorization,
new Credentials(false, null));
new Credentials(false, null, null));
heliumAppFactory.setNotebook(notebook);

View file

@ -98,7 +98,7 @@ public class NotebookTest extends AbstractInterpreterTest implements JobListener
SearchService search = mock(SearchService.class);
notebookRepo = new VFSNotebookRepo(conf);
notebookAuthorization = NotebookAuthorization.init(conf);
credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath());
credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath(), null);
notebook = new Notebook(conf, notebookRepo, schedulerFactory, interpreterFactory, interpreterSettingManager, this, search,
notebookAuthorization, credentials);

View file

@ -103,7 +103,7 @@ public class NotebookRepoSyncTest implements JobListenerFactory {
search = mock(SearchService.class);
notebookRepoSync = new NotebookRepoSync(conf);
notebookAuthorization = NotebookAuthorization.init(conf);
credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath());
credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath(), null);
notebookSync = new Notebook(conf, notebookRepoSync, schedulerFactory, factory, interpreterSettingManager, this, search,
notebookAuthorization, credentials);
anonymous = new AuthenticationInfo("anonymous");