package com.thinkbiganalytics.nifi.v2.sqoop.security;
/*-
* #%L
* thinkbig-nifi-hadoop-processors
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* A utility to generate an encrypted password for use with Sqoop. Uses sqoop recommended procedure.
*/
/*
References:
1) https://github.com/apache/sqoop/blob/trunk/src/java/org/apache/sqoop/util/password/CryptoFileLoader.java
2) http://ingest.tips/2015/03/12/managing-passwords-sqoop/
*/
public class EncryptPassword {
public static void main(String[] args) throws Exception {
/* Basic check of command line arguments */
if (args.length != 3) {
showUsage();
return;
}
/* Get values for command line arguments */
String plainTextPassword = args[0];
String passPhrase = args[1];
String encryptedFileLocation = args[2];
/* Get a secret key factory that is able to convert secret keys of the Key Derivation Algorithm */
SecretKeyFactory factory;
try {
factory = SecretKeyFactory.getInstance(EncryptPasswordConfiguration.KEY_DERIVATION_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Can't load SecretKeyFactory", e);
}
/*
1. Get a PBE (password based encryption) Key Spec
2. Generate a SecretKey using the PBE Key Spec from step 1
3. Generate a SecretKeySpec using the bytes of SecretKey (step2) and desired encryption algorithm
*/
SecretKeySpec key;
try {
key = new SecretKeySpec(
factory.generateSecret(
new PBEKeySpec(passPhrase.toCharArray(),
EncryptPasswordConfiguration.KEY_SALT.getBytes(StandardCharsets.UTF_8),
EncryptPasswordConfiguration.NUM_PBKDF2_ITERATIONS,
EncryptPasswordConfiguration.KEY_LENGTH)
)
.getEncoded(), EncryptPasswordConfiguration.FILE_ENCRYPTION_ALGORITHM_ONLY);
} catch (Exception e) {
throw new IOException("Can't generate secret key", e);
}
/* Get a cipher that implements the desired encryption algorithm */
Cipher crypto;
try {
crypto = Cipher.getInstance(EncryptPasswordConfiguration.FILE_ENCRYPTION_ALGORITHM_FULL);
} catch (Exception e) {
throw new IOException("Can't initialize the cipher", e);
}
byte[] encryptedBytes;
/*
1. Initialize the cipher with the SecretKeySpec (for encrypting)
2. Encrypt the plain text password using the cipher.
*/
try {
crypto.init(Cipher.ENCRYPT_MODE, key);
encryptedBytes = crypto.doFinal(plainTextPassword.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new IOException("Can't encrypt the password", e);
}
/* Write the encrypted password to output file */
FileOutputStream output = new FileOutputStream(new File(encryptedFileLocation));
output.write(encryptedBytes);
output.close();
/* Encoded Base64 string that can be used for configuration */
String base64EncodedEncryptedPassword = Base64.getEncoder().encodeToString(encryptedBytes);
/* Show summary and next step */
System.out.println("The encrypted password location: " + encryptedFileLocation);
System.out.println("The passphrase used (keep this in a safe place): " + passPhrase);
System.out.println("The base64 encoded encrypted password: " + base64EncodedEncryptedPassword);
System.out.println("\nFor Sqoop:\n"
+ "1) Take the encrypted password file and put in HDFS. "
+ "Refer to the HDFS location and passphrase during job runs.\n OR \n"
+ "2) Enter the base64 encoded encrypted password in processor/controller-service configuration");
}
private static void showUsage() {
System.out.println("Usage: 3 arguments needed:\n"
+ "1. Plain Text Password (that needs to be encrypted)\n"
+ "2. Passphrase (will be required for decryption)\n"
+ "3. Location of the encrypted password file");
}
}