Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.POLIS.licensing.frontend; import com.POLIS.licensing.frontend.FrontendSystemConnector; import com.POLIS.licensing.frontend.AnnotationEnabledFrontend; import com.POLIS.licensing.common.decorator.LicenseDecorator; import com.POLIS.licensing.common.annotation.LicenseElement; import com.POLIS.licensing.common.exception.BadLicenseException; import com.POLIS.licensing.common.exception.OperationException; import com.POLIS.licensing.common.exception.SystemStateException; import com.POLIS.licensing.common.license.AbstractSerializationBasedLicense; import com.POLIS.licensing.common.license.AbstractSerializationBasedLicenseFactory; import com.POLIS.licensing.common.license.License; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.codec.binary.Base64; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; /** * * @author Martin */ public class AnnotationEnabledFrontendTest { AnnotationEnabledFrontend<TestLicense> frontend; PublicKey serverPubKey; PrivateKey serverPrivKey; public AnnotationEnabledFrontendTest() { } @BeforeClass public static void setUpClass() { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } @AfterClass public static void tearDownClass() { } @Before public void setUp() throws NoSuchAlgorithmException, NoSuchProviderException, SystemStateException, OperationException { frontend = new AnnotationEnabledFrontend<>(new TestFactory(), new TestConnector(), new TestDecorator()); SecureRandom random = new SecureRandom(); KeyPairGenerator rsagenerator = KeyPairGenerator.getInstance("RSA", "BC"); rsagenerator.initialize(1024, random); KeyPair pair = rsagenerator.generateKeyPair(); serverPubKey = pair.getPublic(); serverPrivKey = pair.getPrivate(); frontend.initialize(serverPubKey); } @After public void tearDown() { } /** * Test of licenseToDisk method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testLicenseToDisk() throws Exception { File licenseFile = new File("request.license"); TestLicense license = frontend.blankLicense(); license.hostname = "test.host"; license.initiator = "some bloke"; license.created = System.currentTimeMillis(); license.signLicense(frontend.getFrontendPrivKey()); frontend.licenseToDisk(licenseFile, license); TestFactory factory = new TestFactory(); String licenseString = ""; TestLicense retrievedLicense; try (FileInputStream stream = new FileInputStream(licenseFile); InputStreamReader input = new InputStreamReader(stream, "UTF-8"); BufferedReader reader = new BufferedReader(input)) { while (reader.ready()) { licenseString = licenseString + reader.readLine(); } } retrievedLicense = factory.loadLicense(licenseString, serverPrivKey, frontend.getFrontendPubKey()); assertTrue("Retrieved license differed from Original", license.equals(retrievedLicense)); } /** * Test of licenseRequest method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testLicenseRequest() throws Exception { TestLicense freshLicense = frontend.blankLicense(); freshLicense.created = System.currentTimeMillis(); freshLicense.hostname = "Test"; freshLicense.id = UUID.randomUUID(); TestLicense request = frontend.licenseRequest(freshLicense); assertTrue("Could not verify license", request.verifyLicense(frontend.getFrontendPubKey())); } /** * Test of loadLicenseFromDisk method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testLoadLicenseFromDisk() throws Exception { //Create license request. File licenseLocation = new File("test.license"); TestLicense freshLicense = frontend.blankLicense(); freshLicense.created = System.currentTimeMillis(); freshLicense.hostname = "Test"; freshLicense.id = UUID.randomUUID(); TestLicense request = frontend.licenseRequest(freshLicense); request.signLicense(frontend.getFrontendPrivKey()); frontend.licenseToDisk(licenseLocation, request); //Load request "server-side" and sign String serverLicenseString = ""; try (FileInputStream stream = new FileInputStream(licenseLocation); InputStreamReader input = new InputStreamReader(stream, "UTF-8"); BufferedReader reader = new BufferedReader(input)) { while (reader.ready()) { serverLicenseString = serverLicenseString + reader.readLine(); } } TestFactory factory = new TestFactory(); TestLicense serverSideLicense = factory.loadLicense(serverLicenseString, serverPrivKey, frontend.getFrontendPubKey()); serverSideLicense.authorizations = new ArrayList<String>( Arrays.asList(new String[] { "Auth1", "Auth2", "Auth3", "Auth4", "Auth5" })); serverSideLicense.signed = System.currentTimeMillis(); serverSideLicense.signLicense(serverPrivKey); String licenseString = serverSideLicense.getEncryptedLicense(frontend.getFrontendPubKey()); try (FileWriter filewriter = new FileWriter(licenseLocation); BufferedWriter writer = new BufferedWriter(filewriter);) { writer.append(licenseString); } //Load the license in frontend frontend.loadLicenseFromDisk(licenseLocation); assertTrue("License in frontend was not the same as on the server", frontend.getCurrentLicense().equals(serverSideLicense)); } /** * Test of checkLicenseAvailable method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testCheckLicenseAvailable1() throws Exception { assertFalse("License should not be available at start", frontend.checkLicenseAvailable()); testLoadLicenseFromDisk(); assertTrue("License should be available after load", frontend.checkLicenseAvailable()); } @Test public void testCheckLicenseAvailable2() throws Exception { assertFalse("License should not be available at start", frontend.checkLicenseAvailable()); testLoadLicenseFromDisk(); frontend.getCurrentLicense().hostname = "ChangingHostnameWithoutResigning"; assertFalse("License should be available. Hostname changed without new server signature", frontend.checkLicenseAvailable()); } /** * Test of load method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testLoad() throws Exception { testLoadLicenseFromDisk(); frontend = new AnnotationEnabledFrontend<>(new TestFactory(), new TestConnector(), new TestDecorator()); assertTrue("Loading failed", frontend.load()); assertTrue("Loading of license failed", frontend.checkLicenseAvailable()); } /** * Test of blankLicense method, of class AbstractAnnotationEnabledFrontend. */ @Test public void testBlankLicense() { TestFactory factory = new TestFactory(); assertTrue("The licenses should be equal", factory.createEmptyLicense().equals(frontend.blankLicense())); } public static class TestLicense extends AbstractSerializationBasedLicense { @LicenseElement(name = "hostname") String hostname; @LicenseElement(name = "ID", mandatory = LicenseElement.Mandatory.BOTH) UUID id; @LicenseElement(name = "creator", mandatory = LicenseElement.Mandatory.REQUEST) String initiator; @LicenseElement(name = "authorizations") List<String> authorizations; @LicenseElement(name = "time_created", mandatory = LicenseElement.Mandatory.REQUEST) Long created; @LicenseElement(name = "time_signed", mandatory = LicenseElement.Mandatory.REQUEST) Long signed; public TestLicense() { super(); } @Override protected String getFieldsAsString() { return (hostname != null ? hostname : "") + (id != null ? id.toString() : "") + (initiator != null ? initiator : "") + (authorizations != null ? authorizations : "") + (created != null ? created : "") + (signed != null ? signed : ""); } @Override public boolean isRequestSignable() throws BadLicenseException { return hostname != null && initiator != null && created != null; } @Override public boolean isLicenseSignable() throws BadLicenseException { return authorizations != null; } @Override public int hashCode() { int hash = 3; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final TestLicense other = (TestLicense) obj; if (!Objects.equals(this.hostname, other.hostname)) { return false; } if (!Objects.equals(this.id, other.id)) { return false; } if (!Objects.equals(this.initiator, other.initiator)) { return false; } if (!Objects.equals(this.authorizations, other.authorizations)) { return false; } if (!Objects.equals(this.created, other.created)) { return false; } if (!Objects.equals(this.signed, other.signed)) { return false; } return true; } } public static class TestFactory extends AbstractSerializationBasedLicenseFactory<TestLicense> { @Override protected TestLicense getDeserializedObject(byte[] objectAsBytes) throws IOException, ClassNotFoundException, SystemStateException { return (TestLicense) this.defaultGetDeserializedObject(objectAsBytes); } @Override public TestLicense createEmptyLicense() { return new TestLicense(); } } public static class TestConnector implements FrontendSystemConnector<TestLicense> { private static final File licenseFile = new File("current.license"); private static final File serverKeyFile = new File("server.key"); private static final File frontendPubKeyFile = new File("pub.key"); private static final File frontendPrivKeyFile = new File("priv.key"); @Override public TestLicense loadLicense(PublicKey clientPublicKey, PrivateKey clientPrivateKey, PublicKey serverPublicKey) throws SystemStateException, OperationException, IOException { TestFactory factory = new TestFactory(); try (FileInputStream stream = new FileInputStream(licenseFile); InputStreamReader input = new InputStreamReader(stream, "UTF-8"); BufferedReader reader = new BufferedReader(input)) { StringBuilder builder = new StringBuilder(); while (reader.ready()) { builder.append(reader.readLine()); } return factory.loadLicense(builder.toString(), clientPrivateKey, serverPublicKey); } catch (BadLicenseException ex) { throw new OperationException("Could not read license", ex); } } @Override public void saveLicense(TestLicense license, PublicKey clientPublicKey, PrivateKey clientPrivateKey, PublicKey serverPublicKey) throws SystemStateException, IOException, OperationException { try (FileOutputStream stream = new FileOutputStream(licenseFile); OutputStreamWriter output = new OutputStreamWriter(stream, "UTF-8"); BufferedWriter writer = new BufferedWriter(output)) { writer.append(license.getEncryptedLicense(clientPublicKey)); } } @Override public void saveServerKey(PublicKey serverPublicKey) throws IOException { this.byteArrayToFile(serverPublicKey.getEncoded(), serverKeyFile); } @Override public void saveClientPublicKey(PublicKey clientPublicKey) throws IOException { this.byteArrayToFile(clientPublicKey.getEncoded(), frontendPubKeyFile); } @Override public void saveClientPrivateKey(PrivateKey clientPrivateKey) throws IOException { this.byteArrayToFile(clientPrivateKey.getEncoded(), frontendPrivKeyFile); } @Override public PublicKey loadServerKey() throws SystemStateException, IOException { PublicKey publicKey; try { publicKey = KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(byteArrayFromFile(TestConnector.serverKeyFile))); } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { throw new SystemStateException("Invalid keyspec", ex); } return publicKey; } @Override public PublicKey loadClientPublicKey() throws SystemStateException, IOException { PublicKey publicKey; try { publicKey = KeyFactory.getInstance("RSA").generatePublic( new X509EncodedKeySpec(byteArrayFromFile(TestConnector.frontendPubKeyFile))); } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { throw new SystemStateException("Invalid keyspec", ex); } return publicKey; } @Override public PrivateKey loadClientPrivateKey() throws SystemStateException, IOException { PrivateKey privkey; try { privkey = KeyFactory.getInstance("RSA").generatePrivate( new PKCS8EncodedKeySpec(byteArrayFromFile(TestConnector.frontendPrivKeyFile))); } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { throw new SystemStateException("Invalid keyspec", ex); } return privkey; } private void byteArrayToFile(byte[] arr, File target) throws FileNotFoundException, UnsupportedEncodingException, IOException { try (FileOutputStream stream = new FileOutputStream(target); OutputStreamWriter output = new OutputStreamWriter(stream, "UTF-8"); BufferedWriter writer = new BufferedWriter(output)) { writer.append(Base64.encodeBase64String(arr)); } } private byte[] byteArrayFromFile(File target) throws FileNotFoundException, UnsupportedEncodingException, IOException { byte[] arr; try (FileInputStream stream = new FileInputStream(target); InputStreamReader input = new InputStreamReader(stream, "UTF-8"); BufferedReader reader = new BufferedReader(input)) { StringBuilder builder = new StringBuilder(); while (reader.ready()) { builder.append(reader.readLine()); } arr = Base64.decodeBase64(builder.toString()); } return arr; } } private static class TestDecorator implements LicenseDecorator<TestLicense> { @Override public HashMap<String, Object> getLicenseData(TestLicense licenseToDecorate) { HashMap<String, Object> decorations = new HashMap<>(); decorations.put("time_signed", System.currentTimeMillis()); return decorations; } @Override public void decorate(TestLicense license) { license.initiator = "BillyBonka"; } } }