Java tutorial
/************************************************************************* * * * EJBCA Community: The OpenSource Certificate Authority * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.cesecore.keybind; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import org.apache.log4j.Logger; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; import org.cesecore.authentication.tokens.AuthenticationToken; import org.cesecore.authentication.tokens.UsernamePrincipal; import org.cesecore.authorization.AuthorizationDeniedException; import org.cesecore.certificates.ca.X509CA; import org.cesecore.certificates.certificate.CertificateCreateSessionRemote; import org.cesecore.certificates.certificate.InternalCertificateStoreSessionRemote; import org.cesecore.certificates.certificate.request.PKCS10RequestMessage; import org.cesecore.certificates.certificate.request.RequestMessage; import org.cesecore.certificates.certificate.request.SimpleRequestMessage; import org.cesecore.certificates.certificate.request.X509ResponseMessage; import org.cesecore.certificates.certificateprofile.CertificateProfileConstants; import org.cesecore.certificates.endentity.EndEntityConstants; import org.cesecore.certificates.endentity.EndEntityInformation; import org.cesecore.certificates.endentity.EndEntityTypes; import org.cesecore.certificates.util.AlgorithmConstants; import org.cesecore.junit.util.CryptoTokenRule; import org.cesecore.junit.util.CryptoTokenTestRunner; import org.cesecore.keybind.impl.OcspKeyBinding; import org.cesecore.keys.token.CryptoTokenManagementSessionRemote; import org.cesecore.keys.util.KeyTools; import org.cesecore.mock.authentication.tokens.TestAlwaysAllowLocalAuthenticationToken; import org.cesecore.util.CertTools; import org.cesecore.util.EjbRemoteHelper; import org.cesecore.util.TraceLogMethodsRule; import org.cesecore.util.ui.DynamicUiProperty; import org.ejbca.core.ejb.ca.sign.SignSessionRemote; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; /** * @see InternalKeyBindingMgmtSession * @version $Id$ */ @RunWith(CryptoTokenTestRunner.class) public class InternalKeyBindingMgmtTest { private static final Logger log = Logger.getLogger(InternalKeyBindingMgmtTest.class); private static final AuthenticationToken alwaysAllowToken = new TestAlwaysAllowLocalAuthenticationToken( new UsernamePrincipal(InternalKeyBindingMgmtTest.class.getSimpleName())); private static final CryptoTokenManagementSessionRemote cryptoTokenManagementSession = EjbRemoteHelper.INSTANCE .getRemoteSession(CryptoTokenManagementSessionRemote.class); private static final InternalKeyBindingMgmtSessionRemote internalKeyBindingMgmtSession = EjbRemoteHelper.INSTANCE .getRemoteSession(InternalKeyBindingMgmtSessionRemote.class); private static final CertificateCreateSessionRemote certificateCreateSession = EjbRemoteHelper.INSTANCE .getRemoteSession(CertificateCreateSessionRemote.class); private static final SignSessionRemote signSession = EjbRemoteHelper.INSTANCE .getRemoteSession(SignSessionRemote.class); private static final InternalCertificateStoreSessionRemote internalCertStoreSession = EjbRemoteHelper.INSTANCE .getRemoteSession(InternalCertificateStoreSessionRemote.class, EjbRemoteHelper.MODULE_TEST); private static final String TESTCLASSNAME = InternalKeyBindingMgmtTest.class.getSimpleName(); private static final String KEYBINDING_TYPE_ALIAS = OcspKeyBinding.IMPLEMENTATION_ALIAS; private static final String PROPERTY_ALIAS = OcspKeyBinding.PROPERTY_NON_EXISTING_GOOD; @Rule public TestRule traceLogMethodsRule = new TraceLogMethodsRule(); @ClassRule public static CryptoTokenRule cryptoTokenRule = new CryptoTokenRule(); private static X509CA x509ca; private static int cryptoTokenId; @BeforeClass public static void beforeClass() throws Throwable { x509ca = cryptoTokenRule.createX509Ca(); cryptoTokenId = x509ca.getCAToken().getCryptoTokenId(); } @AfterClass public static void afterClass() { cryptoTokenRule.cleanUp(); } @Test public void assertTestPreRequisites() throws Exception { // Request all available implementations from server and verify that the implementation we intend to use exists final Map<String, Map<String, DynamicUiProperty<? extends Serializable>>> availableTypesAndProperties = internalKeyBindingMgmtSession .getAvailableTypesAndProperties(); final Map<String, DynamicUiProperty<? extends Serializable>> availableProperties = availableTypesAndProperties .get(KEYBINDING_TYPE_ALIAS); assertNotNull("Expected " + KEYBINDING_TYPE_ALIAS + " to exist on the server for this test.", availableProperties); // Verify that a property we intend to modify exists for our key binding implementation assertTrue( "Expected property " + PROPERTY_ALIAS + " in " + KEYBINDING_TYPE_ALIAS + " to exist on the server for this test.", availableProperties.containsKey(PROPERTY_ALIAS)); } @Test public void activationNotPossibleWithoutCertificateReference() throws Exception { final String TEST_METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName(); final String KEY_BINDING_NAME = TEST_METHOD_NAME; final String KEY_PAIR_ALIAS = TEST_METHOD_NAME; removeInternalKeyBindingByName(alwaysAllowToken, TEST_METHOD_NAME); int internalKeyBindingId = 0; try { // First create a new CryptoToken cryptoTokenManagementSession.createKeyPair(alwaysAllowToken, cryptoTokenId, KEY_PAIR_ALIAS, "RSA2048"); // Create a new InternalKeyBinding with a implementation specific property and bind it to the previously generated key final Map<String, Serializable> dataMap = new LinkedHashMap<String, Serializable>(); dataMap.put(PROPERTY_ALIAS, Boolean.FALSE); internalKeyBindingId = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME, InternalKeyBindingStatus.ACTIVE, null, cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, dataMap, null); // Check that the status is not ACTIVE, despite our request (since no certificate reference was provided) final InternalKeyBinding internalKeyBinding = internalKeyBindingMgmtSession .getInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); assertEquals("Creation of active IKB with without a certificate reference was allowed.", InternalKeyBindingStatus.DISABLED.name(), internalKeyBinding.getStatus().name()); internalKeyBinding.setStatus(InternalKeyBindingStatus.ACTIVE); internalKeyBindingMgmtSession.persistInternalKeyBinding(alwaysAllowToken, internalKeyBinding); final InternalKeyBinding internalKeyBindingUpdated = internalKeyBindingMgmtSession .getInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); assertEquals("Update of active IKB with without a certificate reference was allowed.", InternalKeyBindingStatus.DISABLED.name(), internalKeyBindingUpdated.getStatus().name()); } finally { internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); } } @Test public void workflowIssueCertFromPublicKeyAndUpdate() throws Exception { final String TEST_METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName(); final String KEY_BINDING_NAME = TEST_METHOD_NAME; final String KEY_BINDING_NAME1 = TEST_METHOD_NAME + "1"; final String KEY_BINDING_NAME2 = TEST_METHOD_NAME + "2"; final String KEY_PAIR_ALIAS = TEST_METHOD_NAME; // Clean up old key binding removeInternalKeyBindingByName(alwaysAllowToken, TEST_METHOD_NAME); int internalKeyBindingId = 0; int internalKeyBindingId1 = 0; int internalKeyBindingId2 = 0; String certFpToDelete = null; try { // First create a new CryptoToken cryptoTokenManagementSession.createKeyPair(alwaysAllowToken, cryptoTokenId, KEY_PAIR_ALIAS, "RSA2048"); // Create a new InternalKeyBinding with a implementation specific property and bind it to the previously generated key final Map<String, Serializable> dataMap = new LinkedHashMap<String, Serializable>(); dataMap.put(PROPERTY_ALIAS, Boolean.FALSE); internalKeyBindingId = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME, InternalKeyBindingStatus.ACTIVE, null, cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, dataMap, null); // Get the public key for the key pair currently used in the binding PublicKey publicKey = KeyTools.getPublicKeyFromBytes(internalKeyBindingMgmtSession .getNextPublicKeyForInternalKeyBinding(alwaysAllowToken, internalKeyBindingId)); // Issue a certificate in EJBCA for the public key final EndEntityInformation user = new EndEntityInformation(TESTCLASSNAME + "_" + TEST_METHOD_NAME, "CN=" + TESTCLASSNAME + "_" + TEST_METHOD_NAME, x509ca.getCAId(), null, null, EndEntityTypes.ENDUSER.toEndEntityType(), 1, CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER, EndEntityConstants.TOKEN_USERGEN, 0, null); user.setPassword("foo123"); RequestMessage req = new SimpleRequestMessage(publicKey, user.getUsername(), user.getPassword()); X509Certificate keyBindingCertificate = (X509Certificate) (((X509ResponseMessage) certificateCreateSession .createCertificate(alwaysAllowToken, user, req, X509ResponseMessage.class, signSession.fetchCertGenParams())).getCertificate()); certFpToDelete = CertTools.getFingerprintAsString(keyBindingCertificate); // Ask the key binding to search the database for a new certificate matching its public key final String boundCertificateFingerprint = internalKeyBindingMgmtSession .updateCertificateForInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); // Verify that it was the right certificate it found assertEquals("Wrong certificate was found for InternalKeyBinding", CertTools.getFingerprintAsString(keyBindingCertificate), boundCertificateFingerprint); // ...so now we have a mapping between a certificate in the database and a key pair in a CryptoToken // Try to make a new key binding giving the certificate fingerprint directly internalKeyBindingId1 = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME1, InternalKeyBindingStatus.ACTIVE, CertTools.getFingerprintAsString(keyBindingCertificate), cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, dataMap, null); InternalKeyBindingInfo info = internalKeyBindingMgmtSession.getInternalKeyBindingInfo(alwaysAllowToken, internalKeyBindingId1); assertEquals("Wrong certificate was found for InternalKeyBinding", CertTools.getFingerprintAsString(keyBindingCertificate), info.getCertificateId()); // Try to make a new key binding giving the certificate fingerprint directly, but in upper case instead of the default lower case internalKeyBindingId2 = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME2, InternalKeyBindingStatus.ACTIVE, CertTools.getFingerprintAsString(keyBindingCertificate).toUpperCase(Locale.ENGLISH), cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, dataMap, null); info = internalKeyBindingMgmtSession.getInternalKeyBindingInfo(alwaysAllowToken, internalKeyBindingId2); assertEquals("Wrong certificate was found for InternalKeyBinding", CertTools.getFingerprintAsString(keyBindingCertificate), info.getCertificateId()); } finally { internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId1); internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId2); internalCertStoreSession.removeCertificate(certFpToDelete); } } @Test public void workflowIssueCertFromCsrUpdateAndRenew() throws Exception { final String TEST_METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName(); final String KEY_BINDING_NAME = TEST_METHOD_NAME; final String KEY_PAIR_ALIAS = TEST_METHOD_NAME; final String endEntityId = TESTCLASSNAME + "_" + TEST_METHOD_NAME; // Clean up old key binding removeInternalKeyBindingByName(alwaysAllowToken, TEST_METHOD_NAME); int internalKeyBindingId = 0; String certFpToDelete = null; try { // First create a new CryptoToken cryptoTokenManagementSession.createKeyPair(alwaysAllowToken, cryptoTokenId, KEY_PAIR_ALIAS, "RSA2048"); // Create a new InternalKeyBinding with a implementation specific property and bind it to the previously generated key final Map<String, Serializable> dataMap = new LinkedHashMap<String, Serializable>(); dataMap.put(PROPERTY_ALIAS, Boolean.FALSE); internalKeyBindingId = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME, InternalKeyBindingStatus.ACTIVE, null, cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, dataMap, null); // Add a user to EJBCA for the renewal later on final EndEntityInformation endEntityInformation = new EndEntityInformation(endEntityId, "CN=" + TESTCLASSNAME + "_" + TEST_METHOD_NAME, x509ca.getCAId(), null, null, EndEntityTypes.ENDUSER.toEndEntityType(), 1, CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER, EndEntityConstants.TOKEN_USERGEN, 0, null); endEntityInformation.setPassword("foo123"); // Request a CSR for the key pair // First make a couple of requests with different DN to see that that part works final X500Name x500name = CertTools.stringToBcX500Name("CN=name,O=org,C=SE", false); final byte[] csr = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, x500name.getEncoded()); final JcaPKCS10CertificationRequest jcareq = new JcaPKCS10CertificationRequest(csr); assertEquals("Wrong order of DN, should be X500 with C first", "C=SE,O=org,CN=name", jcareq.getSubject().toString()); final X500Name x500name2 = CertTools.stringToBcX500Name("CN=name,O=org,C=SE", true); final byte[] csr2 = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, x500name2.getEncoded()); final JcaPKCS10CertificationRequest jcareq2 = new JcaPKCS10CertificationRequest(csr2); assertEquals("Wrong order of DN, should be LDAP with CN first", "CN=name,O=org,C=SE", jcareq2.getSubject().toString()); // Now make the request that we will actually use final byte[] csr3 = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, null); final RequestMessage req = new PKCS10RequestMessage(csr3); assertEquals("CN=" + KEY_BINDING_NAME, req.getRequestDN()); X509Certificate keyBindingCertificate = (X509Certificate) (((X509ResponseMessage) certificateCreateSession .createCertificate(alwaysAllowToken, endEntityInformation, req, X509ResponseMessage.class, signSession.fetchCertGenParams())).getCertificate()); certFpToDelete = CertTools.getFingerprintAsString(keyBindingCertificate); // Ask the key binding to search the database for a new certificate matching its public key final String boundCertificateFingerprint = internalKeyBindingMgmtSession .updateCertificateForInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); // Verify that it was the right certificate it found assertEquals("Wrong certificate was found for InternalKeyBinding", CertTools.getFingerprintAsString(keyBindingCertificate), boundCertificateFingerprint); // ...so now we have a mapping between a certificate in the database and a key pair in a CryptoToken // Since we no have a certificate issued by an internal CA, we should be able to renew it final String renewedCertificateFingerprint = internalKeyBindingMgmtSession .renewInternallyIssuedCertificate(alwaysAllowToken, internalKeyBindingId, endEntityInformation); assertNotNull("Renewal returned null which is an undefined state.", renewedCertificateFingerprint); assertFalse("After certificate renewal the same certificate was returned", boundCertificateFingerprint.equals(renewedCertificateFingerprint)); final String actualCertificateFingerprint = internalKeyBindingMgmtSession .getInternalKeyBindingInfo(alwaysAllowToken, internalKeyBindingId).getCertificateId(); assertFalse("After certificate renewal the same certificate still in use.", boundCertificateFingerprint.equals(actualCertificateFingerprint)); // Check DN in generated CSR when we have a bound certificate, should be the DN of the old certificate final byte[] csr4 = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, null); final JcaPKCS10CertificationRequest jcareq4 = new JcaPKCS10CertificationRequest(csr4); assertEquals("Wrong DN, should be from the bound certificate", "CN=" + TESTCLASSNAME + "_" + TEST_METHOD_NAME, jcareq4.getSubject().toString()); } finally { internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); internalCertStoreSession.removeCertificate(certFpToDelete); } } @Test public void workflowIssueCertFromCsrAndImport() throws Exception { final String TEST_METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName(); final String KEY_BINDING_NAME = TEST_METHOD_NAME; final String KEY_PAIR_ALIAS = TEST_METHOD_NAME; // Clean up old key binding removeInternalKeyBindingByName(alwaysAllowToken, TEST_METHOD_NAME); int internalKeyBindingId = 0; String certFpToDelete = null; try { // First create a new CryptoToken cryptoTokenManagementSession.createKeyPair(alwaysAllowToken, cryptoTokenId, KEY_PAIR_ALIAS, "RSA2048"); internalKeyBindingId = internalKeyBindingMgmtSession.createInternalKeyBinding(alwaysAllowToken, KEYBINDING_TYPE_ALIAS, KEY_BINDING_NAME, InternalKeyBindingStatus.ACTIVE, null, cryptoTokenId, KEY_PAIR_ALIAS, AlgorithmConstants.SIGALG_SHA1_WITH_RSA, null, null); log.debug("Created InternalKeyBinding with id " + internalKeyBindingId); // Request a CSR for the key pair final byte[] csr = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, CertTools.stringToBcX500Name("CN=" + KEY_BINDING_NAME + ",O=workflow", true).getEncoded()); // Issue a certificate in EJBCA for the public key final EndEntityInformation user = new EndEntityInformation(TESTCLASSNAME + "_" + TEST_METHOD_NAME, "CN=" + TESTCLASSNAME + "_" + TEST_METHOD_NAME, x509ca.getCAId(), null, null, EndEntityTypes.ENDUSER.toEndEntityType(), 1, CertificateProfileConstants.CERTPROFILE_FIXED_OCSPSIGNER, EndEntityConstants.TOKEN_USERGEN, 0, null); user.setPassword("foo123"); RequestMessage req = new PKCS10RequestMessage(csr); assertEquals("CN=" + KEY_BINDING_NAME + ",O=workflow", req.getRequestDN()); X509Certificate keyBindingCertificate = (X509Certificate) (((X509ResponseMessage) certificateCreateSession .createCertificate(alwaysAllowToken, user, req, X509ResponseMessage.class, signSession.fetchCertGenParams())).getCertificate()); certFpToDelete = CertTools.getFingerprintAsString(keyBindingCertificate); // Import the issued certificate (since it is already in the database, only the pointer will be updated) internalKeyBindingMgmtSession.importCertificateForInternalKeyBinding(alwaysAllowToken, internalKeyBindingId, keyBindingCertificate.getEncoded()); // Fetch the InternalKeyBinding's current certificate mapping String boundCertificateFingerprint = internalKeyBindingMgmtSession .getInternalKeyBindingInfo(alwaysAllowToken, internalKeyBindingId).getCertificateId(); // Verify that it was the right certificate it found assertEquals("Wrong certificate was found for InternalKeyBinding", CertTools.getFingerprintAsString(keyBindingCertificate), boundCertificateFingerprint); // ...so now we have a mapping between a certificate in the database and a key pair in a CryptoToken // A final check that the CSR's subject is normally based on the existing certs for renewals final byte[] csr2 = internalKeyBindingMgmtSession.generateCsrForNextKey(alwaysAllowToken, internalKeyBindingId, null); assertEquals("CN=" + TESTCLASSNAME + "_" + TEST_METHOD_NAME, new PKCS10RequestMessage(csr2).getRequestDN()); } finally { internalKeyBindingMgmtSession.deleteInternalKeyBinding(alwaysAllowToken, internalKeyBindingId); internalCertStoreSession.removeCertificate(certFpToDelete); } } private void removeInternalKeyBindingByName(AuthenticationToken authenticationToken, String name) throws AuthorizationDeniedException { // Clean up old key binding final Integer oldInternalKeyBindingId = internalKeyBindingMgmtSession.getIdFromName(name); if (oldInternalKeyBindingId != null && internalKeyBindingMgmtSession .deleteInternalKeyBinding(alwaysAllowToken, oldInternalKeyBindingId)) { log.info("Removed keybinding with name " + name + "."); } } }