|
|
Hard Token Management Framework, Developers Handbook |
Sidnr / Page no |
|
|
|
|||
|
Uppgjort / Author |
Sekretess / Confidentiality |
||
|
Philip Vendil
|
UNRESTRICTED |
||
|
Godkänd / Authorized |
Datum Date |
Version |
|
|
|
2007-08-01 |
1.1 |
|
Hard
Token Management Framework
Developers
Handbook
Written By
Philip Vendil,
philip@primekey.se
+46709885814
2007-08-01
The Hard Token Management Framework contains the core components to easily develop a smart card managing application that covers the entire life cycle of a smart card or USB dongle.
Since almost every organization probably have an existing work flow in place for issuing access cards for physical access it is often preferred that a new system for VPN and computer log-on access fit in the current routines. And since almost every organization have their own work flow and policies for issuing, revoking access to their system it is very hard to write a ready-made system. Therefore have this framework been created where it is possible to use ready made components that takes care of all the tricky bits of the smart card management and it is only up to the developers to combine them in such a way that conforms with organizations policies.
The design goals of the framework is simplicity (it should be easy for the developer to create simple and easy to use applications for the end users), and flexibility (the framework should be flexible enough to cover all the aspects of hard token management).
The Hard Token Management Framework is an add-on the EJBCA (www.ejbca.org) and is developed as an Java-applet and accessed through a browser.
The intended audience of this document is Java developers interested in creating a smart card managing application. It is preferred that the developer has some knowledge about PKI and smart cards but don't need be an expert in any of the areas.
|
Version |
Date |
Name |
Comment |
|---|---|---|---|
|
1.0 |
2007-07-29 |
Philip Vendil |
First official version of the document |
|
1.1 |
2007-08-01 |
Philip Vendil |
Updated with plug-in:able classes |
|
|
|
|
|
Table of Contents
1 Introduction/Scope 2
1.1Who should read this document. 2
2 Document History 2
3 Overview 4
4 The Main Concepts 4
4.1 Controller 4
4.1.1 org.hardtokenmgmt.core.ui.IController Interface 4
4.1.2Example of a Controller 5
4.1.3 org.hardtokenmgmt.core.ui.BaseController Interface 6
4.2 View 8
4.2.1 BaseView 8
4.2.2 Example of a View 9
4.3 Configuration and Building 11
4.3.1 Configuring the Global Settings 11
4.3.2 Adjusting the Theme of the GUI 11
4.3.3 Language Resources 11
4.4 Other Classes 12
4.4.1 org.hardtokenmgmt.core.ui.HardTokenManagementApplet 12
5 The Interfaces 12
5.1 EJBCAWS Interface 12
5.2 Token Manager Interfaces 20
5.2.1org.hardtokenmgmt.core.token.BaseToken 26
5.2.2Implementing support for your own token. 26
5.3 Global Settings 26
5.4 Administrator Settings 28
5.5 Controller Memory 28
5.6 Local Log 28
6 Using Customized Plug-ins 29
7 Using Eclipse for development 30
8 Existing Components 31
9 Testing the Framework 32
9.1Junit tests 32
9.2Testing Token and PKCS11 implementations 32
10 More Information 34
The Hard Token Management Framework is developed in Java and is basically an applet packaged in a war that is supposed to be deployed to an EJBCA installation.
The Framework is communicating with EJBCA using Web Services calls using client authenticated HTTPS. The hard token management war doesn't have to deployed to the same application server as EJBCA but the end user browser should have access to the EJBCAWS interface.
To access the tokens is done through a PKCS11 interface and the design is that it should be relatively easy to change the token an organization to avoid to be looked to one vendor. But this requires that the vendor have a good PKCS11.
This chapter will discuss the main concepts of the framework that a developer must know about before starting developing. This will be later be complemented in the next chapter with the tool set of interfaces (to access CA, token, settings and so on) used to perform the logic of the different components.
One of the design goal of the framework is to make the application modulized so it will be easy to create a component and to reuse already written ones. A customized component is done be creating a “Controller” that has a “View”. The View is a swing representation of the view of the component. The controller is responsible of all the logic of the component and the view should contain all the visual stuff. Each controller is responsible for delegating the controller to another controller when the current one is done with its processing. It is possible to define the “main”controller that created when the applet starts in the global properties of the framework.
The application can be themed so it isn't necessary to set the basic properties (as colour and font) to each swing component. This is done automatically. See section “Adjusting the Theme of the GUI” for more information.
A Controller is a class in charge of the logic of a components, i.e. what will be done if a button is pressed or a select list is changes that should generate some form of action.
There exists a template view in src/java/org/hardtokenmgmt/core/ui/TemplateController.java that can be copied and used as a skeleton.
In the code snippet below in the IController interface shown but it is strongly recommended that every controller inherits the BaseController class that has a lot of help methods for getting access to the different interfaces and switching controller between controllers.
|
public interface IController {
/** * Method that should return true if the logged in administrator * is authorized to use this controller * * @param admin the certificate of the loggen in administrator. * @return true if the administrator is authorized to this controller */ public boolean isAuthorizedToController(X509Certificate admin);
/** * Method called by the main hard token applet when * it gives the control of the application * * @param class path to the controller calling this controller or null * if this was the main controller. */ public void getControl(String callingController);
/** * Method called by the main applet when it add * the view to the main panel. */ public BaseView getView(); } |
Here follows an example of a simple DummyController. This will be started if you configure the setting controller.main in the file src/resources/globalsettings/global.conf to the the classpath of the controller.
The logic can either be set in the constructor or in the method getControl(....) . The getControl is called every time by the applet class that the controller should get the control of the application while the constructor is only called once during the execution of the applet. In order to access things from the view you have to mark those Swing components as public since they are set to private by default by the Visual Editor. Usually is this applied to buttons and similar components.
When a Controller is finished with its execution it should call the protected method switchControlTo(< class name>.class.getName())
|
public class DummyController1 extends BaseController {
public DummyController1() { super(new DummyView1());
// Add some logic to the button named nextButton getDummyView1().getNextButton().addActionListener new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { LocalLog.debug("Switching control to :+DummyController2.class.getName()); getControllerMemory().putData("USERNAME", getDummyView1().getUsername().getText()); switchControlTo(DummyController2.class.getName()); } });
}
private DummyView1 getDummyView1(){ return (DummyView1) getView(); }
/** * Method called by the Main applet when it's time for this * controller to take control * * @see org.hardtokenmgmt.core.ui.IController#getControl(String) */ public void getControl(String callingController) {
} /** * Method called by the main applet to check that * the administrator is authorized to this controller * * @see org.hardtokenmgmt.core.ui.IController#isAuthorizedToController(X509Certificate) */ public boolean isAuthorizedToController(X509Certificate admin) { return true; }
} |
The BaseController have the following help methods that can be used to ease the work of creating a controller. See the Interfaces chapter for more information about more detailed usage.
|
Method |
Comment |
|
|
Help method to print a debug message to the local log. |
|
|
Help method to print a debug message along with an exception to the local log. |
|
Help method to print a error message to the local log. |
|
|
|
Help method to print a error message along with an exception to the local log. |
|
Help method retrieving the Administrator Settings Interface. |
|
|
Help method returning the instance of the controller memory. |
|
|
String getControllerSetting(String key) |
Returns the value of the given controller setting.
The controller setting should start with the implementing controllers classname (not path) in lowercase.
Ex for the controller named DummyController calling getControllerSetting with value 'testsetting' will lookup the property dummycontroller.testsetting in global.properties. |
|
|
Help method retrieving the EJBCA Interface. |
|
Help method retrieving the Global Settings Interface. |
|
|
IToken getProcessableToken() |
Help
method returning the first processable token. Should only be used by controllers only supporting on processable token |
|
Help method retrieving the Token Manager Interface |
|
|
|
Help method to print a info message to the local log |
|
|
Help method to print a info message along with an exception to the local log |
|
IsAdmin() |
Help method checking if the administrator have the administrator flag set in the administrative privileges. |
|
|
Method that should be used when the controller is complete with its operations and want to give the control to another controller |
A View class represents all the visual details of a component, like where i a button located and which icon will it have. The View classes have been designed to work with Eclipse Visual Editor as much as possible. For instance is language resources, images and theme looked up automatically and can be edited directly.
Tip: Make sure you have the visual editor add-on install and use “Open With” -> “Visual Editor” to open views in WYSIWG mode.
There exists a template view in src/java/org/hardtokenmgmt/core/ui/TemplateView.java that can be copied and used as a skeleton.
Base view is the base class of a View that all views should inherit. It mainly is used to set the theme.
|
public abstract class BaseView extends JPanel {
static{ UIHelper.setTheme(); }
private static final long serialVersionUID = 1L; /** * This is the default constructor */ public BaseView() { super(); }
/** * This method initializes this * * @return void */ protected abstract void initialize();
} |
In the following text box is example code of a view. The only thing that is required is the following line in the initialize method:
this.setSize(new Dimension(UIHelper.getAppletWidth(),UIHelper.getAppletHeight()));
To achieve internationalization of the texts use UIHelper.getText(<property>), this call will automatically look up the property in the language files in src/resources/languages/languagefile_xx.properties, just add the property in the language files and it will shop up in the visual editor.
To add images and icons, place the image in the src/resources/languages/images directory and use the method UIHelper.getImage(“filename”), you should just give the file name and no path. One exception is the top logotype (if you want one there) that should be an image of size 600x132 pixels and should always be fetched with UIHelper.getLogo().
|
public class DummyView1 extends BaseView {
private static final long serialVersionUID = 1L; private JLabel dummyLabel = null; private JButton nextButton = null; private JLabel jLabel = null; private JTextField username = null;
public DummyView1(){ super(); initialize(); }
@Override protected void initialize() { jLabel = new JLabel(); jLabel.setBounds(new Rectangle(133, 198, 357, 18)); jLabel.setText(UIHelper.getText("dummy1.message2")); this.setSize(new Dimension(UIHelper.getAppletWidth(), UIHelper.getAppletHeight())); this.setLayout(null); dummyLabel = new JLabel(); dummyLabel.setText(UIHelper.getText("dummy1.message1")); dummyLabel.setBounds(new Rectangle(133, 175, 355, 17));
this.add(dummyLabel, null); this.add(getNextButton(), null); this.add(jLabel, null); this.add(getUsername(), null); }
public JButton getNextButton() { if (nextButton == null) { nextButton = new JButton(); nextButton.setBounds(new Rectangle(191, 273, 165, 69)); nextButton.setText(UIHelper.getText("dummy1.nextbutton")); } return nextButton; }
public JTextField getUsername() { if (username == null) { username = new JTextField(); username.setBounds(new Rectangle(194, 234, 157, 22)); username.setVisible(true); } return username; }
} |
This view will have the following visual layout:

In the file src/resources/globalsettings/global.properties exists all the global settings used to configure the applet. In this file should all properties be set to the application that is general for all users. Example of properties are supported tokens and which controller that is the main one.
There also exists a property file defining the theme of the Swing GUI so it isn't necessary to define all the characteristics like font, colour for each button and label.
In the directory src/resources/languages are the language resource files. They should be in the format languagefile_xx.properties where xx should be the locale name.
When a language resource is looked up it first checks the file custom_languagefile (see the customization chapter) then in the language file that matches the computers locale and if it isn't found in neither the value in languagefile_en.properties will be used.
This class is the main applet. It takes care of the creation and termination of the applet as well as extracting the information about the logged in administrator. There should be no need to alter this applet class just to configure the starting controller in the global.properties.
The EJBCAWS interface is intended to be access from a controller. If the BaseController is inherited then there exists a protected help method getEJBCAInterface() that returns the interface. Otherwise can the interface be fetched with the static method InterfaceFactory.getEjbcaInterface().
When developing and testing a controller it can be convenient to not use a regular EJBCA installation but a simple dummy interface. This is set in ejbcaws.implementation property of the global.properties file. One dummy implementation that can be used is the org.hardtokenmgmt.core.ejbca.DummyEjbcaWS class.
The EJBCA WS Interface have the following methods.
/**
* Interface the the EJBCA RA WebService. Contains the following methods:
*
* editUser : Edits/adds userdata
* findUser : Retrieves the userdata for a given user.
* findCerts : Retrieves the certificates generated for a user.
* pkcs10Req : Generates a certificate using the given userdata and the public key from the PKCS10
* pkcs12Req : Generates a PKCS12 keystore (with the private key) using the given userdata
* revokeCert : Revokes the given certificate.
* revokeUser : Revokes all certificates for a given user, it's also possible to delete the user.
* revokeToken : Revokes all certificates placed on a given hard token
* checkRevokationStatus : Checks the revokation status of a certificate.
* isAuthorized : Checks if an admin is authorized to an resource
* fetchUserData : Method used to fetch userdata from an existing UserDataSource
* genTokenCertificates : Method used to add information about a generated hardtoken
* existsHardToken : Looks up if a serial number already have been generated
* getHardTokenData : Method fetching information about a hard token given it's hard token serial number.
* getHardTokenDatas: Method fetching all hard token informations for a given user.
* republishCertificate : Method performing a republication of a selected certificate
* isApproved : Looks up if a requested action have been approved by an authorized administrator or not
* customLog : Logs a CUSTOM_LOG event to the logging system
* deleteUserDataFromSource : Method used to remove user data from a user data source
* getCertificate : Returns a certificate given its issuer and serial number
*
* Observere: All methods have to be called using client authenticated https
* otherwise will a AuthorizationDenied Exception be thrown.
*
* @author Philip Vendil
* $Id: IEjbcaWS.java,v 1.6 2007/07/05 05:55:08 herrvendil Exp $
*/
public interface IEjbcaWS {
public static final int CUSTOMLOG_LEVEL_INFO = 1;
public static final int CUSTOMLOG_LEVEL_ERROR = 2;
/**
* Method that should be used to edit/add a user to the EJBCA database,
* if the user doesn't already exists it will be added othervise it will be
* overwritten.
*
* Observe: if the user doesn't already exists, it's status will always be set to 'New'.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/create_end_entity and/or edit_end_entity
* - /endentityprofilesrules/<end entity profile of user>/create_end_entity and/or edit_end_entity
* - /ca/<ca of user>
*
* @param userdata contains all the information about the user about to be added.
* @param clearPwd indicates it the password should be stored in cleartext, requeried
* when creating server generated keystores.
* @throws EjbcaException
*/
public abstract void editUser(UserDataVOWS userdata)
throws AuthorizationDeniedException,
UserDoesntFullfillEndEntityProfile, EjbcaException,
ApprovalException, WaitingForApprovalException;
/**
* Retreives information about a user in the database.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/view_end_entity
* - /endentityprofilesrules/<end entity profile of matching users>/view_end_entity
* - /ca/<ca of matching users>
*
* @param username, the unique username to search for
* @return a array of UserDataVOWS objects (Max 100) containing the information about the user or null if user doesn't exists.
* @throws AuthorizationDeniedException if client isn't authorized to request
* @throws IllegalQueryException if query isn't valid
* @throws EjbcaException
*/
public abstract List<UserDataVOWS> findUser(UserMatch usermatch)
throws AuthorizationDeniedException, IllegalQueryException,
EjbcaException;
/**
* Retreives a collection of certificates generated for a user.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/view_end_entity
* - /endentityprofilesrules/<end entity profile of the user>/view_end_entity
* - /ca/<ca of user>
*
* @param username a unique username
* @param onlyValid only return valid certs not revoked or expired ones.
* @return a collection of X509Certificates or null if no certificates could be found
* @throws AuthorizationDeniedException if client isn't authorized to request
* @throws NotFoundException if user cannot be found
* @throws EjbcaException
*/
public abstract List<Certificate> findCerts(String username,
boolean onlyValid) throws AuthorizationDeniedException,
NotFoundException, EjbcaException;
/**
* Method to use to generate a certificate for a user. The method must be preceded by
* a editUser call, either to set the userstatus to 'new' or to add nonexisting users.
*
* Observe, the user must first have added/set the status to new with edituser command
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/view_end_entity
* - /endentityprofilesrules/<end entity profile of the user>/view_end_entity
* - /ca_functionality/create_certificate
* - /ca/<ca of user>
*
* @param username the unique username
* @param password the password sent with editUser call
* @param pkcs10 the PKCS10 (only the public key is used.)
* @param hardTokenSN If the certificate should be connected with a hardtoken, it is
* possible to map it by give the hardTokenSN here, this will simplyfy revokation of a tokens
* certificates. Use null if no hardtokenSN should be assiciated with the certificate.
* @return the generated certificate.
* @throws AuthorizationDeniedException if client isn't authorized to request
* @throws NotFoundException if user cannot be found
*/
public abstract Certificate pkcs10Req(String username, String password,
String pkcs10, String hardTokenSN)
throws AuthorizationDeniedException, NotFoundException,
EjbcaException;
/**
* Method to use to generate a server generated keystore. The method must be preceded by
* a editUser call, either to set the userstatus to 'new' or to add nonexisting users and
* the users token should be set to SecConst.TOKEN_SOFT_P12.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/view_end_entity
* - /endentityprofilesrules/<end entity profile of the user>/view_end_entity
* - /ca_functionality/create_certificate
* - /ca/<ca of user>
*
* @param username the unique username
* @param password the password sent with editUser call
* @param hardTokenSN If the certificate should be connected with a hardtoken, it is
* possible to map it by give the hardTokenSN here, this will simplyfy revokation of a tokens
* certificates. Use null if no hardtokenSN should be assiciated with the certificate.
* @param keyspec that the generated key should have, examples are 1024 for RSA or prime192v1 for ECDSA.
* @param keyalg that the generated key should have, RSA, ECDSA. Use
one of the constants in
*
CATokenConstants.org.ejbca.core.model.ca.catoken.KEYALGORITHM_XX.
* @return the generated keystore
* @throws AuthorizationDeniedException if client isn't authorized to request
* @throws NotFoundException if user cannot be found
*/
public abstract KeyStore pkcs12Req(String username, String password,
String hardTokenSN, String keyspec, String keyalg)
throws AuthorizationDeniedException, NotFoundException,
EjbcaException;
/**
* Method used to revoke a certificate.
*
* * Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/revoke_end_entity
* - /endentityprofilesrules/<end entity profile of the user owning the cert>/revoke_end_entity
* - /ca/<ca of certificate>
*
* @param issuerDN of the certificate to revoke
* @param certificateSN of the certificate to revoke
* @param reason for revokation, one of RevokedCertInfo.REVOKATION_REASON_ constants,
* or use RevokedCertInfo.NOT_REVOKED to unrevoke a certificate on hold.
* @throws AuthorizationDeniedException if client isn't authorized.
* @throws NotFoundException if certificate doesn't exist
*/
public abstract void revokeCert(String issuerDN, String certificateSN,
int reason) throws AuthorizationDeniedException, NotFoundException,
EjbcaException;
/**
* Method used to revoke all a users certificates. It is also possible to delete
* a user after all certificates have been revoked.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/revoke_end_entity
* - /endentityprofilesrules/<end entity profile of the user>/revoke_end_entity
* - /ca/<ca of users certificate>
*
* @param username unique username i EJBCA
* @param reasonfor revokation, one of RevokedCertInfo.REVOKATION_REASON_ constants
* or use RevokedCertInfo.NOT_REVOKED to unrevoke a certificate on hold.
* @param deleteUser deletes the users after all the certificates have been revoked.
* @throws AuthorizationDeniedException if client isn't authorized.
* @throws NotFoundException if user doesn't exist
*/
public abstract void revokeUser(String username, int reason,
boolean deleteUser) throws AuthorizationDeniedException,
NotFoundException, EjbcaException;
/**
* Method used to revoke all certificates mapped to one hardtoken.
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ra_functionality/revoke_end_entity
* - /endentityprofilesrules/<end entity profile of the user owning the token>/revoke_end_entity
* - /ca/<ca of certificates on token>
*
* @param hardTokenSN of the hardTokenSN
* @param reasonfor revokation, one of RevokedCertInfo.REVOKATION_REASON_ constants
* @throws AuthorizationDeniedException if client isn't authorized.
* @throws NotFoundException if token doesn't exist
*/
public abstract void revokeToken(String hardTokenSN, int reason)
throws RemoteException, AuthorizationDeniedException,
NotFoundException, EjbcaException;
/**
* Method returning the revokestatus for given user
*
* Authorization requirements: the client certificate must have the following priviledges set
* - Administrator flag set
* - /administrator
* - /ca/<ca of certificate>
*
* @param issuerDN
* @param certificateSN a hexadecimal string
* @return the revokestatus of null i certificate doesn't exists.
* @throws AuthorizationDeniedException if client isn't authorized.
* @see org.ejbca.core.protocol.ws.RevokeStatus
*/
public abstract RevokeStatus checkRevokationStatus(String issuerDN,
String certificateSN) throws AuthorizationDeniedException,
EjbcaException;
/**
* Method checking if a user is authorixed to a given resource
*
* Authorization requirements: a valid client certificate
*
* @param resource the access rule to test
* @return true if the user is authorized to the resource othervise false.
* @throws AuthorizationDeniedException if client isn't authorized.
* @see org.ejbca.core.protocol.ws.RevokeStatus
*/
public abstract boolean isAuthorized(String resource) throws EjbcaException;
/**
* Method used to fetch userdata from an existing UserDataSource.
*
* Authorization requirements:
* - Administrator flag set
* - /administrator
* - /userdatasourcesrules/<user data source>/fetch_userdata (for all the given user data sources)
* - /ca/<all cas defined in all the user data sources>
*
* If not turned of in jaxws.properties then only a valid certificate required
*
*
* @param userDataSourceNames a List of User Data Source Names
* @param searchString to identify the userdata.
* @return a List of UserDataSourceVOWS of the data in the specified UserDataSources, if no user data is found will an empty list be
* returned.
* @throws UserDataSourceException if an error occured connecting to one of
* UserDataSources.
*/
public abstract List<UserDataSourceVOWS> fetchUserData(
List<String> userDataSourceNames, String searchString)
throws UserDataSourceException, EjbcaException, AuthorizationDeniedException;
/**
* Method used to add information about a generated hardtoken
*