blob: e2dcde5d76ea4a721920a825f7ffbb101d02ecf0 [file] [log] [blame]
/**
* 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.hadoop.crypto.key;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.UUID;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.security.ProviderUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TestKeyShell {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private PrintStream initialStdOut;
private PrintStream initialStdErr;
/* The default JCEKS provider - for testing purposes */
private String jceksProvider;
@Before
public void setup() throws Exception {
outContent.reset();
errContent.reset();
final File tmpDir = GenericTestUtils.getTestDir(UUID.randomUUID()
.toString());
if (!tmpDir.mkdirs()) {
throw new IOException("Unable to create " + tmpDir);
}
final Path jksPath = new Path(tmpDir.toString(), "keystore.jceks");
jceksProvider = "jceks://file" + jksPath.toUri();
initialStdOut = System.out;
initialStdErr = System.err;
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@After
public void cleanUp() throws Exception {
System.setOut(initialStdOut);
System.setErr(initialStdErr);
}
/**
* Delete a key from the default jceksProvider
* @param ks The KeyShell instance
* @param keyName The key to delete
* @throws Exception
*/
private void deleteKey(KeyShell ks, String keyName) throws Exception {
int rc;
outContent.reset();
final String[] delArgs =
{"delete", keyName, "-f", "-provider", jceksProvider};
rc = ks.run(delArgs);
assertEquals(0, rc);
assertTrue(outContent.toString().contains(keyName + " has been " +
"successfully deleted."));
}
/**
* Lists the keys in the jceksProvider
* @param ks The KeyShell instance
* @param wantMetadata True if you want metadata returned with the keys
* @return The output from the "list" call
* @throws Exception
*/
private String listKeys(KeyShell ks, boolean wantMetadata) throws Exception {
int rc;
outContent.reset();
final String[] listArgs = {"list", "-provider", jceksProvider };
final String[] listArgsM = {"list", "-metadata", "-provider", jceksProvider };
rc = ks.run(wantMetadata ? listArgsM : listArgs);
assertEquals(0, rc);
return outContent.toString();
}
@Test
public void testKeySuccessfulKeyLifecycle() throws Exception {
int rc = 0;
String keyName = "key1";
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
outContent.reset();
final String[] args1 = {"create", keyName, "-provider", jceksProvider};
rc = ks.run(args1);
assertEquals(0, rc);
assertTrue(outContent.toString().contains(keyName + " has been " +
"successfully created"));
assertTrue(outContent.toString()
.contains(ProviderUtils.NO_PASSWORD_WARN));
assertTrue(outContent.toString()
.contains(ProviderUtils.NO_PASSWORD_INSTRUCTIONS_DOC));
assertTrue(outContent.toString()
.contains(ProviderUtils.NO_PASSWORD_CONT));
String listOut = listKeys(ks, false);
assertTrue(listOut.contains(keyName));
listOut = listKeys(ks, true);
assertTrue(listOut.contains(keyName));
assertTrue(listOut.contains("description"));
assertTrue(listOut.contains("created"));
outContent.reset();
final String[] args2 = {"roll", keyName, "-provider", jceksProvider};
rc = ks.run(args2);
assertEquals(0, rc);
assertTrue(outContent.toString().contains("key1 has been successfully " +
"rolled."));
deleteKey(ks, keyName);
listOut = listKeys(ks, false);
assertFalse(listOut, listOut.contains(keyName));
}
/* HADOOP-10586 KeyShell didn't allow -description. */
@Test
public void testKeySuccessfulCreationWithDescription() throws Exception {
outContent.reset();
final String[] args1 = {"create", "key1", "-provider", jceksProvider,
"-description", "someDescription"};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(0, rc);
assertTrue(outContent.toString().contains("key1 has been successfully " +
"created"));
String listOut = listKeys(ks, true);
assertTrue(listOut.contains("description"));
assertTrue(listOut.contains("someDescription"));
}
@Test
public void testInvalidKeySize() throws Exception {
final String[] args1 = {"create", "key1", "-size", "56", "-provider",
jceksProvider};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(1, rc);
assertTrue(outContent.toString().contains("key1 has not been created."));
}
@Test
public void testInvalidCipher() throws Exception {
final String[] args1 = {"create", "key1", "-cipher", "LJM", "-provider",
jceksProvider};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(1, rc);
assertTrue(outContent.toString().contains("key1 has not been created."));
}
@Test
public void testInvalidProvider() throws Exception {
final String[] args1 = {"create", "key1", "-cipher", "AES", "-provider",
"sdff://file/tmp/keystore.jceks"};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(1, rc);
assertTrue(outContent.toString().contains(KeyShell.NO_VALID_PROVIDERS));
}
@Test
public void testTransientProviderWarning() throws Exception {
final String[] args1 = {"create", "key1", "-cipher", "AES", "-provider",
"user:///"};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(0, rc);
assertTrue(outContent.toString().contains("WARNING: you are modifying a " +
"transient provider."));
}
@Test
public void testTransientProviderOnlyConfig() throws Exception {
final String[] args1 = {"create", "key1"};
int rc = 0;
KeyShell ks = new KeyShell();
Configuration config = new Configuration();
config.set(KeyProviderFactory.KEY_PROVIDER_PATH, "user:///");
ks.setConf(config);
rc = ks.run(args1);
assertEquals(1, rc);
assertTrue(outContent.toString().contains(KeyShell.NO_VALID_PROVIDERS));
}
@Test
public void testStrict() throws Exception {
outContent.reset();
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
final String[] args1 = {"create", "hello", "-provider", jceksProvider,
"-strict"};
rc = ks.run(args1);
assertEquals(1, rc);
assertTrue(outContent.toString()
.contains(ProviderUtils.NO_PASSWORD_ERROR));
assertTrue(outContent.toString()
.contains(ProviderUtils.NO_PASSWORD_INSTRUCTIONS_DOC));
}
@Test
public void testFullCipher() throws Exception {
final String keyName = "key1";
final String[] args1 = {"create", keyName, "-cipher", "AES/CBC/pkcs5Padding",
"-provider", jceksProvider};
int rc = 0;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
rc = ks.run(args1);
assertEquals(0, rc);
assertTrue(outContent.toString().contains(keyName + " has been " +
"successfully created"));
deleteKey(ks, keyName);
}
@Test
public void testAttributes() throws Exception {
int rc;
KeyShell ks = new KeyShell();
ks.setConf(new Configuration());
/* Simple creation test */
final String[] args1 = {"create", "keyattr1", "-provider", jceksProvider,
"-attr", "foo=bar"};
rc = ks.run(args1);
assertEquals(0, rc);
assertTrue(outContent.toString().contains("keyattr1 has been " +
"successfully created"));
/* ...and list to see that we have the attr */
String listOut = listKeys(ks, true);
assertTrue(listOut.contains("keyattr1"));
assertTrue(listOut.contains("attributes: [foo=bar]"));
/* Negative tests: no attribute */
outContent.reset();
final String[] args2 = {"create", "keyattr2", "-provider", jceksProvider,
"-attr", "=bar"};
rc = ks.run(args2);
assertEquals(1, rc);
/* Not in attribute = value form */
outContent.reset();
args2[5] = "foo";
rc = ks.run(args2);
assertEquals(1, rc);
/* No attribute or value */
outContent.reset();
args2[5] = "=";
rc = ks.run(args2);
assertEquals(1, rc);
/* Legal: attribute is a, value is b=c */
outContent.reset();
args2[5] = "a=b=c";
rc = ks.run(args2);
assertEquals(0, rc);
listOut = listKeys(ks, true);
assertTrue(listOut.contains("keyattr2"));
assertTrue(listOut.contains("attributes: [a=b=c]"));
/* Test several attrs together... */
outContent.reset();
final String[] args3 = {"create", "keyattr3", "-provider", jceksProvider,
"-attr", "foo = bar",
"-attr", " glarch =baz ",
"-attr", "abc=def"};
rc = ks.run(args3);
assertEquals(0, rc);
/* ...and list to ensure they're there. */
listOut = listKeys(ks, true);
assertTrue(listOut.contains("keyattr3"));
assertTrue(listOut.contains("[foo=bar]"));
assertTrue(listOut.contains("[glarch=baz]"));
assertTrue(listOut.contains("[abc=def]"));
/* Negative test - repeated attributes should fail */
outContent.reset();
final String[] args4 = {"create", "keyattr4", "-provider", jceksProvider,
"-attr", "foo=bar",
"-attr", "foo=glarch"};
rc = ks.run(args4);
assertEquals(1, rc);
/* Clean up to be a good citizen */
deleteKey(ks, "keyattr1");
deleteKey(ks, "keyattr2");
deleteKey(ks, "keyattr3");
}
}