/* * 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.brooklyn.util.core.crypto; import java.io.File; import java.io.FileInputStream; import java.nio.charset.Charset; import java.security.KeyPair; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.brooklyn.util.core.ResourceUtils; import org.apache.brooklyn.util.core.crypto.FluentKeySigner; import org.apache.brooklyn.util.core.crypto.SecureKeys; import org.apache.brooklyn.util.core.crypto.SecureKeys.PassphraseProblem; import org.apache.brooklyn.util.crypto.AuthorizedKeysParser; import org.apache.brooklyn.util.os.Os; import org.testng.Assert; import org.testng.annotations.Test; import com.google.common.io.Files; public class SecureKeysAndSignerTest { // a bit slow, so marked as integration (but possibly due to leftover rebind-cleanup, benign failures writing to /tmp/xx) @Test(groups="Integration") public void testGenerateSignedKeys() throws Exception { FluentKeySigner signer = new FluentKeySigner("the-root"). validForYears(2). selfsign(); X509Certificate signerCert = signer.getAuthorityCertificate(); KeyPair aKey = SecureKeys.newKeyPair(); X509Certificate aCert = signer.newCertificateFor("A", aKey); KeyPair bKey = SecureKeys.newKeyPair(); X509Certificate bCert = signer.newCertificateFor("B", bKey); FluentKeySigner selfSigner1 = new FluentKeySigner("self1").selfsign(); X509Certificate selfCert1 = selfSigner1.getAuthorityCertificate(); SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA"); SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { signerCert }, "RSA"); try { SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { bCert }, "RSA"); Assert.fail("Trust manager for A should not accept B"); } catch (CertificateException e) { /* expected */ } // SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA"); // NB, the above failes; we have to convert to a canonical implementation, handled by the following Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(signerCert, signerCert)); Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, signerCert)); Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(bCert, signerCert)); Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(signerCert, aCert)); Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(bCert, aCert)); Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(selfCert1, selfCert1)); Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(selfCert1, signerCert)); } @Test public void testInjectCertificateAuthority() throws Exception { KeyPair caKey = SecureKeys.newKeyPair(); X509Certificate caCert = new FluentKeySigner("the-root", caKey).selfsign().getAuthorityCertificate(); FluentKeySigner signer = new FluentKeySigner(caCert, caKey); Assert.assertEquals("the-root", signer.getCommonName()); KeyPair aKey = SecureKeys.newKeyPair(); X509Certificate aCert = signer.newCertificateFor("A", aKey); Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, caCert)); } @Test public void testReadRsaKey() throws Exception { KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null); checkNonTrivial(key); } @Test(expectedExceptions=IllegalStateException.class) public void testReadRsaPublicKeyAsPemFails() throws Exception { // should fail; see next test SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"), null); } @Test public void testReadRsaPublicKeyAsAuthKeysWorks() throws Exception { PublicKey key = AuthorizedKeysParser.decodePublicKey( ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub")); KeyPair fromPem = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null); Assert.assertEquals(key, fromPem.getPublic()); } @Test public void testEncodeDecodeRsaPublicKey() throws Exception { String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"); PublicKey key = AuthorizedKeysParser.decodePublicKey(data); String data2 = AuthorizedKeysParser.encodePublicKey(key); Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'"); PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2); Assert.assertEquals(key2, key); } @Test public void testEncodeDecodeDsaPublicKey() throws Exception { String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_dsa.pem.pub"); PublicKey key = AuthorizedKeysParser.decodePublicKey(data); String data2 = AuthorizedKeysParser.encodePublicKey(key); Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'"); PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2); Assert.assertEquals(key2, key); } @Test public void testReadDsaKey() throws Exception { KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_dsa.pem"), null); checkNonTrivial(key); } @Test(expectedExceptions=Exception.class) public void testCantReadRsaPassphraseKeyWithoutPassphrase() throws Exception { KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null); checkNonTrivial(key); } @Test(expectedExceptions=PassphraseProblem.class) public void testReadRsaPassphraseWithoutKeyFails() throws Exception { SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null); } @Test public void testReadRsaPassphraseKeyAndWriteWithoutPassphrase() throws Exception { KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), "passphrase"); checkNonTrivial(key); File f = Os.newTempFile(getClass(), "brooklyn-sample_rsa_passphrase_without_passphrase.pem"); Files.write(SecureKeys.stringPem(key), f, Charset.defaultCharset()); KeyPair key2 = SecureKeys.readPem(new FileInputStream(f), null); checkNonTrivial(key2); Assert.assertEquals(key2.getPrivate().getEncoded(), key.getPrivate().getEncoded()); Assert.assertEquals(key2.getPublic().getEncoded(), key.getPublic().getEncoded()); } private void checkNonTrivial(KeyPair key) { Assert.assertNotEquals(key.getPrivate().getEncoded().length, 0); Assert.assertNotEquals(key.getPublic().getEncoded().length, 0); } }