//* Licensed Materials - Property of * //* IBM * //* Miracle A/S * //* Alexandra Instituttet A/S * //* * //* eu.abc4trust.pabce.1.34 * //* * //* (C) Copyright IBM Corp. 2014. All Rights Reserved. * //* (C) Copyright Miracle A/S, Denmark. 2014. All Rights Reserved. * //* (C) Copyright Alexandra Instituttet A/S, Denmark. 2014. All * //* Rights Reserved. * //* US Government Users Restricted Rights - Use, duplication or * //* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * //* * //* This file is licensed 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 eu.abc4trust.services; import java.io.File; import java.io.IOException; import java.net.URI; import java.security.SecureRandom; import java.util.List; import java.util.Random; import java.util.Set; import java.util.logging.Logger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Element; import org.xml.sax.SAXException; import eu.abc4trust.abce.internal.user.credentialManager.CredentialManagerException; import eu.abc4trust.abce.internal.user.policyCredentialMatcher.PolicyCredentialMatcherImpl; import eu.abc4trust.abce.utils.SecretWrapper; import eu.abc4trust.cryptoEngine.CryptoEngineException; import eu.abc4trust.exceptions.CannotSatisfyPolicyException; import eu.abc4trust.guice.ProductionModuleFactory.CryptoEngine; import eu.abc4trust.keyManager.KeyManager; import eu.abc4trust.keyManager.KeyManagerException; import eu.abc4trust.returnTypes.IssuanceReturn; import eu.abc4trust.returnTypes.ObjectFactoryReturnTypes; import eu.abc4trust.returnTypes.UiIssuanceReturn; import eu.abc4trust.returnTypes.UiPresentationArguments; import eu.abc4trust.returnTypes.UiPresentationReturn; import eu.abc4trust.ri.servicehelper.AbstractHelper; import eu.abc4trust.ri.servicehelper.user.UserHelper; import eu.abc4trust.services.helpers.UserDebugger; import eu.abc4trust.smartcard.BasicSmartcard; import eu.abc4trust.xml.ABCEBoolean; import eu.abc4trust.xml.CredentialDescription; import eu.abc4trust.xml.CredentialSpecification; import eu.abc4trust.xml.InspectorPublicKey; import eu.abc4trust.xml.IssuanceMessage; import eu.abc4trust.xml.IssuanceMessageAndBoolean; import eu.abc4trust.xml.IssuerParameters; import eu.abc4trust.xml.ObjectFactory; import eu.abc4trust.xml.PresentationPolicyAlternatives; import eu.abc4trust.xml.PresentationToken; import eu.abc4trust.xml.RevocationAuthorityParameters; import eu.abc4trust.xml.SystemParameters; import eu.abc4trust.xml.URISet; import eu.abc4trust.xml.util.XmlUtils; @Path("/user") public class UserService { private static final CryptoEngine CRYPTO_ENGINE = CryptoEngine.IDEMIX; private final ObjectFactory of = new ObjectFactory(); private final Logger log = Logger.getLogger(UserService.class.getName()); private final String fileStoragePrefix = Constants.USER_STORAGE_FOLDER + "/"; private static final String USERNAME = "default-user"; /** * This method, on input a presentation policy p, decides whether the * credentials in the Users credential store could be used to produce a * valid presentation token satisfying the policy p. If so, this method * returns true, otherwise, it returns false. * * @param p * @return */ @POST() @Path("/canBeSatisfied/") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> canBeSatisfied( JAXBElement<PresentationPolicyAlternatives> rawPresentationPolicyAlternatives) { this.log.info("UserService - canBeSatisfied "); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { PresentationPolicyAlternatives p = rawPresentationPolicyAlternatives.getValue(); boolean b = instance.getEngine().canBeSatisfied(USERNAME, p); ABCEBoolean createABCEBoolean = this.of.createABCEBoolean(); createABCEBoolean.setValue(b); return this.of.createABCEBoolean(createABCEBoolean); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CryptoEngineException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method, on input a presentation policy alternatives p, returns an * argument to be passed to the UI for choosing how to satisfy the policy, * or returns an error if the policy cannot be satisfied (if the * canBeSatisfied method would have returned false). For returning such an * argument, this method will investigate whether the User has the necessary * credentials and/or established pseudonyms to create one or more (e.g., by * satisfying different alternatives in the policy, or by using different * sets of credentials to satisfy one alternative) presentation tokens that * satisfiy the policy. * * The return value of this method should be passed to the User Interface * (or to some other component that is capable of rendering a * UiPresentationReturn object from a UiPresentationArguments object). The * return value of the UI must then be passed to the method * createPresentationToken(UiPresentationReturn) for creating a presentation * token. * * @param p * @return * @throws CannotSatisfyPolicyException * @throws CredentialManagerException * @throws KeyManagerException */ @POST() @Path("/createPresentationToken/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<UiPresentationArguments> createPresentationToken( JAXBElement<PresentationPolicyAlternatives> rawPresentationPolicyAlternatives) { this.log.info("UserService - createPresentationToken "); PresentationPolicyAlternatives presentationPolicyAlternatives = rawPresentationPolicyAlternatives.getValue(); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { UiPresentationArguments uiPresentationArguments = instance.getEngine().createPresentationToken(USERNAME, presentationPolicyAlternatives); return ObjectFactoryReturnTypes.wrap(uiPresentationArguments); } catch (CannotSatisfyPolicyException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (KeyManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CryptoEngineException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @POST() @Path("/createPresentationTokenUi/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<PresentationToken> createPresentationTokenFromUi( JAXBElement<UiPresentationReturn> rawUpr) { this.log.info("UserService - createPresentationTokenUi "); UiPresentationReturn uiPresentationReturn = rawUpr.getValue(); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { URI uid = URI .create("http://ticketcompany/MyFavoriteSoccerTeam/issuance:idemix"); IssuerParameters ip = instance.keyManager.getIssuerParameters(uid); String s = XmlUtils.toXml(this.of.createIssuerParameters(ip)); System.out.println(s); } catch (KeyManagerException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (JAXBException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (SAXException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } try { PresentationToken presentationToken = instance.getEngine() .createPresentationToken(USERNAME, uiPresentationReturn); return this.of.createPresentationToken(presentationToken); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CryptoEngineException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method performs one step in an interactive issuance protocol. On * input an incoming issuance message im obtained from the Issuer, it either * returns the outgoing issuance message that is to be sent back to the * Issuer, an object that must be sent to the User Interface (UI) to allow * the user to decide how to satisfy a policy (or confirm the only choice), * or returns a description of the newly issued credential at successful * completion of the protocol. In the first case, the Context attribute of * the outgoing message has the same value as that of the incoming message, * allowing the Issuer to link the different messages of this issuance * protocol. * * If this is the first time this method is called for a given context, the * method expects the issuance message to contain an issuance policy, and * returns an object that is to be sent to the UI (allowing the user to * chose his preferred way of generating the presentation token, or to * confirm the only possible choice). * * This method throws an exception if the policy cannot be satisfied with * the user's current credentials. * * If this method returns an IssuanceMessage, that message should be * forwarded to the Issuer. If this method returns a CredentialDescription, * then the issuance protocol was successful. If this method returns a * UiIssuanceArguments, that object must be forwarded to the UI (or to some * other component that is capable of rendering a UiIssuanceReturn object * from a UiIssuanceArguments object); the method * issuanceProtocolStep(UiIssuanceReturn) should then be invoked with the * object returned by the UI. * * @param im * @return * @throws CannotSatisfyPolicyException * @throws CryptoEngineException * @throws KeyManagerException * @throws CredentialManagerException */ @POST() @Path("/issuanceProtocolStep/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<IssuanceReturn> issuanceProtocolStep( JAXBElement<IssuanceMessage> jm) { this.log.info("UserService - issuanceProtocolStep - IssuanceMessage"); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); IssuanceMessage m = jm.getValue(); try { IssuanceReturn issuanceReturn = instance.getEngine() .issuanceProtocolStep(USERNAME, m); return ObjectFactoryReturnTypes.wrap(issuanceReturn); } catch (CannotSatisfyPolicyException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CryptoEngineException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } catch (KeyManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @POST() @Path("/issuanceProtocolStepUi/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<IssuanceMessage> issuanceProtocolStep( UiIssuanceReturn uir) { this.log.info("UserService - issuanceProtocolStep - UiIssuanceReturn"); ; this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { IssuanceMessage issuanceMessage = instance.getEngine() .issuanceProtocolStep(USERNAME, uir); return new ObjectFactory().createIssuanceMessage(issuanceMessage); } catch (CryptoEngineException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method updates the non-revocation evidence associated to all * credentials in the credential store. Calling this method at regular time * intervals reduces the likelihood of having to update non-revocation * evidence at the time of presentation, thereby not only speeding up the * presentation process, but also offering improved privacy as the * Revocation Authority is no longer pinged at the moment of presentation. * */ @POST() @Path("/updateNonRevocationEvidence/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public void updateNonRevocationEvidence() { this.log.info("UserService - updateNonRevocationEvidence "); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { instance.credentialManager.updateNonRevocationEvidence(USERNAME); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method returns an array of all unique credential identifiers (UIDs) * available in the Credential Manager. * * @return */ @GET() @Path("/listCredentials/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<URISet> listCredentials() { this.log.info("UserService - listCredentials "); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); List<URI> credentialUids; try { credentialUids = instance.credentialManager.listCredentials(USERNAME); URISet uriList = this.of.createURISet(); uriList.getURI().addAll(credentialUids); return this.of.createURISet(uriList); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method returns the description of the credential with the given * unique identifier. The unique credential identifier credUid is the * identifier which was included in the credential description that was * returned at successful completion of the issuance protocol. * * @param credUid * @return */ @GET() @Path("/getCredentialDescription/{credentialUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<CredentialDescription> getCredentialDescription( @PathParam("credentialUid") URI credUid) { this.log.info("UserService - getCredentialDescription "); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { CredentialDescription credDesc = instance.credentialManager .getCredentialDescription(USERNAME, credUid); return this.of.createCredentialDescription(credDesc); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @POST() @Path("/createSmartcard/{issuerParametersUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public void createSmartcard( @PathParam("issuerParametersUid") URI issuerParametersUid) { this.log.info("UserService - getCredentialDescription "); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { Random random = new SecureRandom(); KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); SystemParameters systemParameters = keyManager .getSystemParameters(); systemParameters.getCryptoParams().getContent().get(1); SecretWrapper secretWrapper = new SecretWrapper(random, systemParameters); IssuerParameters issuerParameters = keyManager .getIssuerParameters(issuerParametersUid); secretWrapper.addIssuerParameters(issuerParameters, systemParameters); BasicSmartcard softwareSmartcard = secretWrapper .getSoftwareSmartcard(); instance.cardStorage.addSmartcard(softwareSmartcard, 1234); File file = new File(this.fileStoragePrefix + File.separatorChar + "smartcard"); // TODO verify this is required // SmartcardInitializeTool.storeObjectInFile(softwareSmartcard, file); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } /** * This method deletes the credential with the given identifier from the * credential store. If deleting is not possible (e.g. if the referred * credential does not exist) the method returns false, and true otherwise. * * @param credentialUid * @return */ @DELETE() @Path("/deleteCredential/{credentialUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> deleteCredential( @PathParam("credentialUid") URI credentialUid) { this.log.info("UserService - deleteCredential \"" + credentialUid + "\""); this.initializeHelper(); UserHelper instance = UserHelper.getInstance(); try { boolean r = instance.credentialManager.deleteCredential(USERNAME, credentialUid); ABCEBoolean createABCEBoolean = this.of .createABCEBoolean(); createABCEBoolean.setValue(r); return this.of.createABCEBoolean(createABCEBoolean); } catch (CredentialManagerException ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @PUT() @Path("/storeCredentialSpecification/{credentialSpecifationUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> storeCredentialSpecification( @PathParam("credentialSpecifationUid") URI credentialSpecifationUid, CredentialSpecification credSpec) { this.log.info("UserService - storeCredentialSpecification "); try { KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); boolean r = keyManager.storeCredentialSpecification( credentialSpecifationUid, credSpec); ABCEBoolean createABCEBoolean = this.of .createABCEBoolean(); createABCEBoolean.setValue(r); return this.of.createABCEBoolean(createABCEBoolean); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @POST() @Path("/storeSystemParameters/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> storeSystemParameters( SystemParameters systemParameters) { this.log.info("UserService - storeSystemParameters "); try { KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); boolean r = keyManager .storeSystemParameters(systemParameters); ABCEBoolean createABCEBoolean = this.of.createABCEBoolean(); createABCEBoolean.setValue(r); if (r) { this.initializeHelper(); } return this.of.createABCEBoolean(createABCEBoolean); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @PUT() @Path("/storeIssuerParameters/{issuerParametersUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> storeIssuerParameters( @PathParam("issuerParametersUid") URI issuerParametersUid, IssuerParameters issuerParameters) { this.log.info("UserService - storeIssuerParameters "); this.log.info("UserService - storeIssuerParameters - issuerParametersUid: " + issuerParametersUid + ", " + issuerParameters.getParametersUID()); try { KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); boolean r = keyManager.storeIssuerParameters(issuerParametersUid, issuerParameters); ABCEBoolean createABCEBoolean = this.of .createABCEBoolean(); createABCEBoolean.setValue(r); this.log.info("UserService - storeIssuerParameters - done "); return this.of.createABCEBoolean(createABCEBoolean); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @PUT() @Path("/storeRevocationAuthorityParameters/{revocationAuthorityParametersUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> storeRevocationAuthorityParameters( @PathParam("revocationAuthorityParametersUid") URI revocationAuthorityParametersUid, RevocationAuthorityParameters revocationAuthorityParameters) { this.log.info("UserService - storeRevocationAuthorityParameters: \"" + revocationAuthorityParameters + "\""); try { KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); boolean r = keyManager.storeRevocationAuthorityParameters( revocationAuthorityParametersUid, revocationAuthorityParameters); ABCEBoolean createABCEBoolean = this.of.createABCEBoolean(); createABCEBoolean.setValue(r); return this.of.createABCEBoolean(createABCEBoolean); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } @PUT() @Path("/storeInspectorPublicKey/{inspectorPublicKeyUid}") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<ABCEBoolean> storeInspectorPublicKey( @PathParam("inspectorPublicKeyUid") URI inspectorPublicKeyUid, InspectorPublicKey inspectorPublicKey) { this.log.info("UserService - storeInspectorPublicKey "); this.log.info("UserService - storeInspectorPublicKey - inspectorPublicKeyUid: " + inspectorPublicKeyUid + ", " + inspectorPublicKey.getPublicKeyUID()); try { KeyManager keyManager = UserStorageManager .getKeyManager(this.fileStoragePrefix); boolean r = keyManager.storeInspectorPublicKey(inspectorPublicKeyUid, inspectorPublicKey); ABCEBoolean createABCEBoolean = this.of .createABCEBoolean(); createABCEBoolean.setValue(r); this.log.info("UserService - storeInspectorPublicKey - done "); return this.of.createABCEBoolean(createABCEBoolean); } catch (Exception ex) { throw new WebApplicationException(ex, Response.Status.INTERNAL_SERVER_ERROR); } } private void initializeHelper() { this.log.info("UserService loading..."); try { // Disable non-device-bound secrets. PolicyCredentialMatcherImpl.GENERATE_SECRET_IF_NONE_EXIST = false; if (UserHelper.isInit()) { this.log.info("UserHelper is initialized"); AbstractHelper.verifyFiles(false, this.fileStoragePrefix); } else { this.log.info("Initializing UserHelper..."); UserHelper.initInstanceForService(CRYPTO_ENGINE, this.fileStoragePrefix); this.log.info("UserHelper is initialized"); } UserHelper instance = UserHelper.getInstance(); Set<URI> keySet = instance.cardStorage .getSmartcards().keySet(); for (URI uri : keySet) { System.out.println("Smartcards: " + uri); } } catch (Exception ex) { System.out.println("Create UserHelper FAILED " + ex); ex.printStackTrace(); } UserHelper instance = UserHelper.getInstance(); } @POST() @Path("/extractIssuanceMessage/") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Produces(MediaType.TEXT_XML) public JAXBElement<IssuanceMessage> extractIssuanceMessage( JAXBElement<IssuanceMessageAndBoolean> rawIssuanceMessageAndBoolean) throws JAXBException, SAXException, ParserConfigurationException, IOException { IssuanceMessageAndBoolean issuanceMessageAndBoolean = rawIssuanceMessageAndBoolean.getValue(); IssuanceMessage issuanceMessage = issuanceMessageAndBoolean .getIssuanceMessage(); ObjectFactory of = new ObjectFactory(); // String issuanceMessageAsXML = XmlUtils.toNormalizedXML(of // .createIssuanceMessage(issuanceMessage)); return of.createIssuanceMessage(issuanceMessage); } }