blob: cb7caf31695dffb21f735d875497f915afcef520 [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.ozone.security;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenIdentifier;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.util.Time;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
/**
* Test class for OzoneManagerDelegationToken.
*/
public class TestOzoneManagerBlockToken {
private static final Logger LOG = LoggerFactory
.getLogger(TestOzoneManagerBlockToken.class);
private static final String BASEDIR = GenericTestUtils
.getTempPath(TestOzoneManagerBlockToken.class.getSimpleName());
private static final String KEYSTORES_DIR =
new File(BASEDIR).getAbsolutePath();
private static long expiryTime;
private static KeyPair keyPair;
private static X509Certificate cert;
private static final long MAX_LEN = 1000;
@BeforeClass
public static void setUp() throws Exception {
File base = new File(BASEDIR);
FileUtil.fullyDelete(base);
base.mkdirs();
expiryTime = Time.monotonicNow() + 60 * 60 * 24;
// Create Ozone Master key pair.
keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
// Create Ozone Master certificate (SCM CA issued cert) and key store.
cert = KeyStoreTestUtil
.generateCertificate("CN=OzoneMaster", keyPair, 30, "SHA256withRSA");
}
@After
public void cleanUp() {
}
@Test
public void testSignToken() throws GeneralSecurityException, IOException {
String keystore = new File(KEYSTORES_DIR, "keystore.jks")
.getAbsolutePath();
String truststore = new File(KEYSTORES_DIR, "truststore.jks")
.getAbsolutePath();
String trustPassword = "trustPass";
String keyStorePassword = "keyStorePass";
String keyPassword = "keyPass";
KeyStoreTestUtil.createKeyStore(keystore, keyStorePassword, keyPassword,
"OzoneMaster", keyPair.getPrivate(), cert);
// Create trust store and put the certificate in the trust store
Map<String, X509Certificate> certs = Collections.singletonMap("server",
cert);
KeyStoreTestUtil.createTrustStore(truststore, trustPassword, certs);
// Sign the OzoneMaster Token with Ozone Master private key
PrivateKey privateKey = keyPair.getPrivate();
OzoneBlockTokenIdentifier tokenId = new OzoneBlockTokenIdentifier(
"testUser", "84940",
EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
byte[] signedToken = signTokenAsymmetric(tokenId, privateKey);
// Verify a valid signed OzoneMaster Token with Ozone Master
// public key(certificate)
boolean isValidToken = verifyTokenAsymmetric(tokenId, signedToken, cert);
LOG.info("{} is {}", tokenId, isValidToken ? "valid." : "invalid.");
// Verify an invalid signed OzoneMaster Token with Ozone Master
// public key(certificate)
tokenId = new OzoneBlockTokenIdentifier("", "",
EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
LOG.info("Unsigned token {} is {}", tokenId,
verifyTokenAsymmetric(tokenId, RandomUtils.nextBytes(128), cert));
}
public byte[] signTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
PrivateKey privateKey) throws NoSuchAlgorithmException,
InvalidKeyException, SignatureException {
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
rsaSignature.initSign(privateKey);
rsaSignature.update(tokenId.getBytes());
byte[] signature = rsaSignature.sign();
return signature;
}
public boolean verifyTokenAsymmetric(OzoneBlockTokenIdentifier tokenId,
byte[] signature, Certificate certificate) throws InvalidKeyException,
NoSuchAlgorithmException, SignatureException {
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
rsaSignature.initVerify(certificate);
rsaSignature.update(tokenId.getBytes());
boolean isValid = rsaSignature.verify(signature);
return isValid;
}
private byte[] signTokenSymmetric(OzoneBlockTokenIdentifier identifier,
Mac mac, SecretKey key) {
try {
mac.init(key);
} catch (InvalidKeyException ike) {
throw new IllegalArgumentException("Invalid key to HMAC computation",
ike);
}
return mac.doFinal(identifier.getBytes());
}
OzoneBlockTokenIdentifier generateTestToken() {
return new OzoneBlockTokenIdentifier(RandomStringUtils.randomAlphabetic(6),
RandomStringUtils.randomAlphabetic(5),
EnumSet.allOf(HddsProtos.BlockTokenSecretProto.AccessModeProto.class),
expiryTime, cert.getSerialNumber().toString(), MAX_LEN);
}
@Test
public void testAsymmetricTokenPerf() throws NoSuchAlgorithmException,
CertificateEncodingException, NoSuchProviderException,
InvalidKeyException, SignatureException {
final int testTokenCount = 1000;
List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
List<byte[]> tokenPasswordAsym = new ArrayList<>();
for (int i = 0; i < testTokenCount; i++) {
tokenIds.add(generateTestToken());
}
KeyPair kp = KeyStoreTestUtil.generateKeyPair("RSA");
// Create Ozone Master certificate (SCM CA issued cert) and key store
X509Certificate omCert;
omCert = KeyStoreTestUtil.generateCertificate("CN=OzoneMaster",
kp, 30, "SHA256withRSA");
long startTime = Time.monotonicNowNanos();
for (int i = 0; i < testTokenCount; i++) {
tokenPasswordAsym.add(
signTokenAsymmetric(tokenIds.get(i), kp.getPrivate()));
}
long duration = Time.monotonicNowNanos() - startTime;
LOG.info("Average token sign time with HmacSha256(RSA/1024 key) is {} ns",
duration / testTokenCount);
startTime = Time.monotonicNowNanos();
for (int i = 0; i < testTokenCount; i++) {
verifyTokenAsymmetric(tokenIds.get(i), tokenPasswordAsym.get(i), omCert);
}
duration = Time.monotonicNowNanos() - startTime;
LOG.info("Average token verify time with HmacSha256(RSA/1024 key) "
+ "is {} ns", duration / testTokenCount);
}
@Test
public void testSymmetricTokenPerf() {
String hmacSHA1 = "HmacSHA1";
String hmacSHA256 = "HmacSHA256";
testSymmetricTokenPerfHelper(hmacSHA1, 64);
testSymmetricTokenPerfHelper(hmacSHA256, 1024);
}
public void testSymmetricTokenPerfHelper(String hmacAlgorithm, int keyLen) {
final int testTokenCount = 1000;
List<OzoneBlockTokenIdentifier> tokenIds = new ArrayList<>();
List<byte[]> tokenPasswordSym = new ArrayList<>();
for (int i = 0; i < testTokenCount; i++) {
tokenIds.add(generateTestToken());
}
KeyGenerator keyGen;
try {
keyGen = KeyGenerator.getInstance(hmacAlgorithm);
keyGen.init(keyLen);
} catch (NoSuchAlgorithmException nsa) {
throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
" algorithm.");
}
Mac mac;
try {
mac = Mac.getInstance(hmacAlgorithm);
} catch (NoSuchAlgorithmException nsa) {
throw new IllegalArgumentException("Can't find " + hmacAlgorithm +
" algorithm.");
}
SecretKey secretKey = keyGen.generateKey();
long startTime = Time.monotonicNowNanos();
for (int i = 0; i < testTokenCount; i++) {
tokenPasswordSym.add(
signTokenSymmetric(tokenIds.get(i), mac, secretKey));
}
long duration = Time.monotonicNowNanos() - startTime;
LOG.info("Average token sign time with {}({} symmetric key) is {} ns",
hmacAlgorithm, keyLen, duration / testTokenCount);
}
}