summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java2
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/bku/online/webapp/BKURequestHandler.java20
-rw-r--r--BKUOnline/src/main/webapp/HTTP-ohne.html11
-rw-r--r--BKUOnline/src/main/webapp/appletPage.jsp29
-rw-r--r--STAL/src/main/java/at/gv/egiz/stal/util/JCEAlgorithmNames.java4
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java3
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java17
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java4
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java42
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java2
-rw-r--r--bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java8
-rw-r--r--bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java3
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java348
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java169
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java38
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/LockedException.java38
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java44
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java339
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SWCard.java79
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java2
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java223
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java3
-rw-r--r--smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java92
-rw-r--r--smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java8
-rw-r--r--smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java10
25 files changed, 1149 insertions, 389 deletions
diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java
index 8ae9d7a3..2054ae86 100644
--- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java
@@ -46,7 +46,7 @@ class PINDocument extends PlainDocument {
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
- if (pinSpec.getMaxLength() >= (getLength() + str.length())) {
+ if (pinSpec.getMaxLength() < 0 || pinSpec.getMaxLength() >= (getLength() + str.length())) {
boolean matches = true;
for (int i = 0; i < str.length(); i++) {
Matcher m = pinPattern.matcher(str.substring(i, i + 1));
diff --git a/BKUOnline/src/main/java/at/gv/egiz/bku/online/webapp/BKURequestHandler.java b/BKUOnline/src/main/java/at/gv/egiz/bku/online/webapp/BKURequestHandler.java
index 6f3b9d7f..9092e3f9 100644
--- a/BKUOnline/src/main/java/at/gv/egiz/bku/online/webapp/BKURequestHandler.java
+++ b/BKUOnline/src/main/java/at/gv/egiz/bku/online/webapp/BKURequestHandler.java
@@ -44,6 +44,8 @@ import at.gv.egiz.org.apache.tomcat.util.http.AcceptLanguage;
*/
public class BKURequestHandler extends SpringBKUServlet {
+ private static final long serialVersionUID = 1L;
+
public final static String REDIRECT_URL = "appletPage.jsp";
protected Log log = LogFactory.getLog(BKURequestHandler.class);
@@ -105,6 +107,8 @@ public class BKURequestHandler extends SpringBKUServlet {
String width = getStringFromStream(bindingProcessor.getFormData("appletWidth"), charset);
String height = getStringFromStream(bindingProcessor.getFormData("appletHeight"), charset);
String background = getStringFromStream(bindingProcessor.getFormData("appletBackground"), charset);
+ String guiStyle = getStringFromStream(bindingProcessor.getFormData("appletGuiStyle"), charset);
+ String hashDataDisplay = getStringFromStream(bindingProcessor.getFormData("appletHashDataDisplay"), charset);
if (width != null) {
try {
log.trace("Found applet width parameter: " + width);
@@ -124,12 +128,16 @@ public class BKURequestHandler extends SpringBKUServlet {
}
}
if (background != null) {
- try {
- log.trace("Found applet background parameter: " + background);
- session.setAttribute("appletBackground", background);
- } catch (NumberFormatException nfe) {
- log.warn(nfe);
- }
+ log.trace("Found applet background parameter: " + background);
+ session.setAttribute("appletBackground", background);
+ }
+ if (guiStyle != null) {
+ log.trace("Found applet GUI style parameter: " + guiStyle);
+ session.setAttribute("appletGuiStyle", guiStyle);
+ }
+ if (hashDataDisplay != null) {
+ log.trace("Found applet hash data display parameter: " + hashDataDisplay);
+ session.setAttribute("appletHashDataDisplay", hashDataDisplay);
}
resp.sendRedirect(REDIRECT_URL);
diff --git a/BKUOnline/src/main/webapp/HTTP-ohne.html b/BKUOnline/src/main/webapp/HTTP-ohne.html
index 1923113e..044432ce 100644
--- a/BKUOnline/src/main/webapp/HTTP-ohne.html
+++ b/BKUOnline/src/main/webapp/HTTP-ohne.html
@@ -92,8 +92,17 @@ legend {
name="appletHeight" value="130" id="appletHeight">
<p><label for="appletBackground">Applet Background</label> <input
name="appletBackground" value="" id="appletBackground">
+<p>
+ <label for="appletGuiStyle">GUI Style</label>
+ <input type="radio" name="appletGuiStyle" value="simple" checked="checked">simple
+ <input type="radio" name="appletGuiStyle" value="advanced">advanced
+</p>
+<p>
+ <label for="appletHashDataDisplay">GUI Style</label>
+ <input type="radio" name="appletHashDataDisplay" value="external" checked="checked">external
+ <input type="radio" name="appletHashDataDisplay" value="internal">internal
+</p>
-</textarea></p>
<!--
<p><label for="RedirectURL">RedirectURL</label> <input
name="RedirectURL" value="" id="RedirectURL"></p>
diff --git a/BKUOnline/src/main/webapp/appletPage.jsp b/BKUOnline/src/main/webapp/appletPage.jsp
index 950141c4..ffb67828 100644
--- a/BKUOnline/src/main/webapp/appletPage.jsp
+++ b/BKUOnline/src/main/webapp/appletPage.jsp
@@ -20,7 +20,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<title>MOCCA Appletpage</title>
+<title>MOCCA Applet</title>
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon">
<script type="text/javascript" src="js/deployJava.js"></script>
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
@@ -29,19 +29,18 @@
</head>
<body>
<%
- int width = session.getAttribute("appletWidth") == null
- ? 190
- : (Integer) session.getAttribute("appletWidth");
- int height = session.getAttribute("appletHeight") == null
- ? 130
- : (Integer) session.getAttribute("appletHeight");
- String backgroundImg = (String) session
- .getAttribute("appletBackground");
+ int width = session.getAttribute("appletWidth") == null ? 190
+ : (Integer) session.getAttribute("appletWidth");
+ int height = session.getAttribute("appletHeight") == null ? 130
+ : (Integer) session.getAttribute("appletHeight");
+ String backgroundImg = (String) session.getAttribute("appletBackground");
+ String guiStyle = (String) session.getAttribute("appletGuiStyle");
+ String hashDataDisplay = (String) session.getAttribute("appletHashDataDisplay");
%>
<script>
if (!deployJava.versionCheck('1.6.0_04+')) {
document
- .write('<b>Diese Anwendung benötigt die Java Platform Version 1.6.0 oder höher.</b>' + '<input type="submit" value="Java Platform 1.6.0_02 installieren" onclick="deployJava.installLatestJRE();">');
+ .write('<b>Diese Anwendung benötigt die Java Platform Version 1.6.0_04 oder höher.</b>' + '<input type="submit" value="Java Platform 1.6.0_02 installieren" onclick="deployJava.installLatestJRE();">');
} else {
var attributes = {
codebase :'applet',
@@ -51,15 +50,15 @@
height :<%=height%>
};
var parameters = {
- GuiStyle : 'simple',
+ GuiStyle : '<%=guiStyle%>',
Background : '<%=backgroundImg%>',
- WSDL_URL :'../stal?wsdl',
- HashDataDisplay : 'external',
- HashDataURL : '../hashDataInput',
+ WSDL_URL : '../stal?wsdl',
+ HashDataDisplay : '<%=hashDataDisplay%>',
+ HashDataURL : '../hashDataInput',
SessionID : '<%=session.getId()%>',
RedirectURL : '../bkuResult'
};
- var version = '1.6.0_02';
+ var version = '1.6.0_04';
deployJava.runApplet(attributes, parameters, version);
}
</script>
diff --git a/STAL/src/main/java/at/gv/egiz/stal/util/JCEAlgorithmNames.java b/STAL/src/main/java/at/gv/egiz/stal/util/JCEAlgorithmNames.java
index 7e8a767e..c162eed4 100644
--- a/STAL/src/main/java/at/gv/egiz/stal/util/JCEAlgorithmNames.java
+++ b/STAL/src/main/java/at/gv/egiz/stal/util/JCEAlgorithmNames.java
@@ -31,7 +31,7 @@ public class JCEAlgorithmNames {
public static String[] SHA_1_ALGORITMS = {
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1",
- "http://www.w3.org/2000/09/xmldsig#sha1" };
+ "http://www.w3.org/2000/09/xmldsig#rsa-sha1" };
private static JCEAlgorithmNames instance = new JCEAlgorithmNames();
@@ -42,7 +42,7 @@ public class JCEAlgorithmNames {
}
public static String getJCEHashName(String xmlAlgorithmURI) {
- return instance.hashNameMap.get(xmlAlgorithmURI);
+ return instance.hashNameMap.get(xmlAlgorithmURI);
}
public void registerHash(String xmlAlgorithmURI, String jceName) {
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java
index 5e44e82b..4a22874c 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java
@@ -564,6 +564,7 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements
SLCommandContext commandCtx = new SLCommandContext();
commandCtx.setSTAL(getSTAL());
commandCtx.setURLDereferencerContext(new SimpleFormDataContextImpl(this));
+ commandCtx.setLocale(locale);
slCommand = SLCommandFactory.getInstance().createSLCommand(source,
commandCtx);
log.debug("Created new command: " + slCommand);
@@ -731,7 +732,7 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements
protected void handleBindingProcessorError(OutputStream os, String encoding,
Templates templates) throws IOException {
log.debug("Writing error as result");
- ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError);
+ ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError, locale);
error.writeTo(new StreamResult(new OutputStreamWriter(os, encoding)), templates);
}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java
index c95736bd..5af2afac 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java
@@ -16,13 +16,17 @@
*/
package at.gv.egiz.bku.slcommands;
+import java.util.Locale;
+
import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
import at.gv.egiz.stal.STAL;
public class SLCommandContext {
private STAL stal;
- private URLDereferencerContext urlDerefCtx;
+ private URLDereferencerContext urlDerefCtx;
+
+ private Locale locale;
public void setSTAL(STAL aStal) {
this.stal = aStal;
@@ -38,5 +42,14 @@ public class SLCommandContext {
public URLDereferencerContext getURLDereferencerContext() {
return urlDerefCtx;
- }
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
} \ No newline at end of file
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java
index 628326cf..6462bcf6 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java
@@ -216,8 +216,8 @@ public class CreateXMLSignatureCommandImpl extends SLCommandImpl<CreateXMLSignat
return new CreateXMLSignatureResultImpl(signature.getDocument());
- } catch (SLException e) {
- return new ErrorResultImpl(e);
+ } catch (SLException e) {
+ return new ErrorResultImpl(e, cmdCtx.getLocale());
}
}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java
index 176ba001..5d0f0de0 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java
@@ -16,6 +16,8 @@
*/
package at.gv.egiz.bku.slcommands.impl;
+import java.util.Locale;
+
import javax.xml.transform.Result;
import javax.xml.transform.Templates;
@@ -32,22 +34,34 @@ public class ErrorResultImpl extends SLResultImpl implements ErrorResult {
/**
* The exception containing information provided in the <code>ErrorResponse</code>.
*/
- protected SLException slException;
-
- /**
- * Creates a new instance of this ErrorResultImpl with the given
- * <code>slException</code> containing information provided in the
- * <code>ErrorResponse</code>.
- *
- * @param slException the exception
- */
- public ErrorResultImpl(SLException slException) {
- this.slException = slException;
- }
-
+ protected SLException slException;
+
+ /**
+ * The locale to be used for rendering an <code>ErrorResponse</code>.
+ */
+ protected Locale locale;
+
+ /**
+ * Creates a new instance of this ErrorResultImpl with the given
+ * <code>slException</code> containing information provided in the
+ * <code>ErrorResponse</code> and the <code>locale</code> for rendering
+ * the <code>ErrorResponse</code>.
+ *
+ * @param slException the exception
+ * @param locale the locale
+ */
+ public ErrorResultImpl(SLException slException, Locale locale) {
+ this.slException = slException;
+ this.locale = locale;
+ }
+
@Override
public void writeTo(Result result, Templates templates) {
- writeErrorTo(slException, result, templates);
+ if (locale == null) {
+ writeErrorTo(slException, result, templates);
+ } else {
+ writeErrorTo(slException, result, templates, locale);
+ }
}
} \ No newline at end of file
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java
index d23c0598..c7bb5205 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java
@@ -279,7 +279,7 @@ public class InfoboxReadCommandImpl extends SLCommandImpl<InfoboxReadRequestType
throw new SLCommandException(4000);
}
} catch (SLCommandException e) {
- return new ErrorResultImpl(e);
+ return new ErrorResultImpl(e, cmdCtx.getLocale());
}
}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java
index 57309182..7306b237 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java
@@ -16,6 +16,8 @@
*/
package at.gv.egiz.bku.slcommands.impl;
+import java.util.Locale;
+
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
@@ -181,6 +183,10 @@ public abstract class SLResultImpl implements SLResult {
}
protected void writeErrorTo(SLException slException, Result result, Templates templates) {
+ writeErrorTo(slException, result, templates, Locale.getDefault());
+ }
+
+ protected void writeErrorTo(SLException slException, Result result, Templates templates, Locale locale) {
TransformerHandler transformerHandler = null;
if (templates != null) {
@@ -195,7 +201,7 @@ public abstract class SLResultImpl implements SLResult {
ObjectFactory factory = new ObjectFactory();
ErrorResponseType responseType = factory.createErrorResponseType();
responseType.setErrorCode(slException.getErrorCode());
- responseType.setInfo(slException.getDetailedMsg());
+ responseType.setInfo(slException.getLocalizedMessage(locale));
JAXBElement<ErrorResponseType> response = factory.createErrorResponse(responseType);
Marshaller marshaller = getMarshaller();
diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java
index 8455e934..f10ca520 100644
--- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java
+++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java
@@ -17,6 +17,7 @@
package at.gv.egiz.bku.slcommands.impl;
import java.io.ByteArrayOutputStream;
+import java.util.Locale;
import javax.xml.transform.stream.StreamResult;
@@ -31,7 +32,7 @@ public class ErrorResultImplTest {
public void writeTo() {
SLException slException = new SLException(0,"test.noerror", null);
- ErrorResult errorResult = new ErrorResultImpl(slException);
+ ErrorResult errorResult = new ErrorResultImpl(slException, Locale.getDefault());
ByteArrayOutputStream stream = new ByteArrayOutputStream();
StreamResult result = new StreamResult(stream);
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
index abe086ee..9e56701f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
@@ -30,12 +30,18 @@ package at.gv.egiz.smcc;
import java.nio.charset.Charset;
+import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ private static Log log = LogFactory.getLog(ACOSCard.class);
public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00,
(byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E };
@@ -97,7 +103,145 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
super("at/gv/egiz/smcc/ACOSCard");
}
- byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName)
+ */
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException {
+
+ byte[] aid;
+ byte[] efc;
+ int maxsize;
+ if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ aid = AID_SIG;
+ efc = EF_C_CH_DS;
+ maxsize = EF_C_CH_DS_MAX_SIZE;
+ } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ aid = AID_DEC;
+ efc = EF_C_CH_EKEY;
+ maxsize = EF_C_CH_EKEY_MAX_SIZE;
+ } else {
+ throw new IllegalArgumentException("Keybox " + keyboxName
+ + " not supported.");
+ }
+
+ log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" +
+ " (AID=" + toString(aid) + " EF=" + toString(efc) + ").");
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFile(aid, efc, maxsize + 15000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String)
+ */
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
+ throws SignatureCardException {
+
+ if ("IdentityLink".equals(infobox)) {
+
+ PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name"));
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider,
+ spec, EF_INFOBOX_MAX_SIZE);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
+ PINProvider provider) throws SignatureCardException {
+
+ if (hash.length != 20) {
+ throw new IllegalArgumentException("Hash value must be of length 20.");
+ }
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
+
+ // SELECT DF
+ selectFileFID(DF_SIG);
+ // VERIFY
+ verifyPIN(provider, new PINSpec(6, 10, "[0-9]", getResourceBundle()
+ .getString("sig.pin.name")), KID_PIN_SIG);
+ // MSE: SET DST
+ mseSetDST(0x81, 0xb6, DST_SIG);
+ // PSO: HASH
+ psoHash(hash);
+ // PSO: COMPUTE DIGITAL SIGNATURE
+ return psoComputDigitalSiganture();
+
+ } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
+
+ // SELECT DF
+ selectFileFID(DF_DEC);
+ // VERIFY
+ verifyPIN(provider, new PINSpec(4, 4, "[0-9]", getResourceBundle()
+ .getString("dec.pin.name")), KID_PIN_DEC);
+ // MSE: SET DST
+ mseSetDST(0x41, 0xa4, DST_DEC);
+ // INTERNAL AUTHENTICATE
+ return internalAuthenticate(hash);
+
+
+ // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 54 26 F0 EA AF EA F0 4E D4 A1 AD BF 66 D4 A5 9B 45 6F AF 79 00
+ // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 DF 8C AB 8F E2 AD AC 7B 5A AF BE E9 44 5E 95 99 FA AF 2F 48 00
+
+ } else {
+ throw new IllegalArgumentException("KeyboxName '" + keyboxName
+ + "' not supported.");
+ }
+
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ }
+
+ protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
0x00, fid, 256));
@@ -109,18 +253,40 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
}
}
- byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,
+ return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,
0x00, fid, 256));
- if (resp.getSW() == 0x6a82) {
- throw new SignatureCardException("Failed to select file (FID="
- + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ")");
+ }
+
+ protected int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException {
+
+ CardChannel channel = getCardChannel();
+
+ byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII"));
+ byte[] encodedPIN = new byte[8];
+ System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length,
+ encodedPIN.length));
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00,
+ kid, encodedPIN), false);
+
+ if (resp.getSW() == 0x63c0) {
+ throw new LockedException("PIN locked.");
+ } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ // return number of possible retries
+ return resp.getSW2() & 0x0f;
+ } else if (resp.getSW() == 0x6983) {
+ throw new NotActivatedException();
+ } else if (resp.getSW() == 0x9000) {
+ return -1;
} else {
- return resp.getBytes();
+ throw new SignatureCardException("Failed to verify pin: SW="
+ + Integer.toHexString(resp.getSW()) + ".");
}
- }
+ }
+
/**
*
* @param pinProvider
@@ -128,56 +294,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
* the PIN spec to be given to the pinProvider
* @param kid
* the KID (key identifier) of the PIN to be verified
- * @param kfpc
- * acutal value of the KFCP (key fault presentation counter) or less
- * than 0 if actual value is unknown
- *
- * @return -1 if the PIN has been verifyed successfully, or else the new value
- * of the KFCP (key fault presentation counter)
- *
* @throws CancelledException
* if the user canceld the operation
* @throws javax.smartcardio.CardException
* @throws at.gv.egiz.smcc.SignatureCardException
*/
- int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc)
+ protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid)
throws CardException, CancelledException, SignatureCardException {
- CardChannel channel = getCardChannel();
-
- // get PIN
- String pin = pinProvider.providePIN(spec, kfpc);
- if (pin == null) {
- // User canceld operation
- // throw new CancelledException("User canceld PIN entry");
- return -2;
- }
-
- byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII"));
- byte[] encodedPIN = new byte[8];
- System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length,
- encodedPIN.length));
-
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00,
- kid, encodedPIN));
- if (resp.getSW1() == (byte) 0x63 && resp.getSW2() >> 4 == (byte) 0xc) {
- return resp.getSW2() & (byte) 0x0f;
- } else if (resp.getSW() == 0x6983) {
- // PIN blocked
- throw new SignatureCardException(spec.getLocalizedName() + " blocked.");
- } else if (resp.getSW() != 0x9000) {
- throw new SignatureCardException("Failed to verify pin: SW="
- + Integer.toHexString(resp.getSW()) + ".");
- } else {
- return -1;
- }
-
+ int retries = -1;
+ do {
+ String pin = pinProvider.providePIN(spec, retries);
+ if (pin == null) {
+ // user canceled operation
+ throw new CancelledException("User canceled operation");
+ }
+ retries = verifyPIN(pin, kid);
+ } while (retries > 0);
+
}
-
- void mseSetDST(byte[] dst) throws CardException, SignatureCardException {
+
+ void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81,
- 0xB6, dst));
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, p1,
+ p2, dst));
if (resp.getSW() != 0x9000) {
throw new SignatureCardException("MSE:SET DST failed: SW="
+ Integer.toHexString(resp.getSW()));
@@ -207,102 +347,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
return resp.getData();
}
}
-
- public byte[] getCertificate(KeyboxName keyboxName)
- throws SignatureCardException {
-
- if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
- return readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE);
- } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
- return readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE);
- } else {
- throw new IllegalArgumentException("Keybox " + keyboxName
- + " not supported.");
- }
-
- }
-
- public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
- throws SignatureCardException {
-
- if ("IdentityLink".equals(infobox)) {
-
- PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString(
- "inf.pin.name"));
- try {
- byte[] res = readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider,
- spec, EF_INFOBOX_MAX_SIZE);
- return res;
- } catch (Exception e) {
- throw new SecurityException(e);
- }
-
+
+ byte[] internalAuthenticate(byte[] hash) throws CardException, SignatureCardException {
+ byte[] digestInfo = new byte[] {
+ (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B, (byte) 0x0E,
+ (byte) 0x03, (byte) 0x02, (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04
+ };
+
+ byte[] data = new byte[digestInfo.length + hash.length + 1];
+
+ System.arraycopy(digestInfo, 0, data, 0, digestInfo.length);
+ data[digestInfo.length] = (byte) hash.length;
+ System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length);
+
+ CardChannel channel = getCardChannel();
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + Integer.toHexString(resp.getSW()));
} else {
- throw new IllegalArgumentException("Infobox '" + infobox
- + "' not supported.");
+ return resp.getData();
}
-
}
public String toString() {
return "a-sign premium";
}
-
- public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
- PINProvider provider) throws SignatureCardException {
-
- if (hash.length != 20) {
- throw new IllegalArgumentException("Hash value must be of length 20");
- }
-
- byte[] fid;
- byte kid;
- byte[] dst;
- PINSpec spec;
- if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
- fid = DF_SIG;
- kid = KID_PIN_SIG;
- dst = DST_SIG;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString(
- "sig.pin.name"));
-
- } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
- fid = DF_DEC;
- kid = KID_PIN_DEC;
- dst = DST_DEC;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString(
- "dec.pin.name"));
-
- } else {
- throw new IllegalArgumentException("KeyboxName '" + keyboxName
- + "' not supported.");
- }
-
- try {
-
- // SELECT DF
- selectFileFID(fid);
- // VERIFY
- int kfpc = -1;
- while (true) {
- kfpc = verifyPIN(provider, spec, kid, kfpc);
- if (kfpc < -1) {
- return null;
- } else if (kfpc < 0) {
- break;
- }
- }
- // MSE: SET DST
- mseSetDST(dst);
- // PSO: HASH
- psoHash(hash);
- // PSO: COMPUTE DIGITAL SIGNATURE
- byte[] rs = psoComputDigitalSiganture();
-
- return rs;
-
- } catch (CardException e) {
- throw new SignatureCardException("Failed to create signature.", e);
- }
- }
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
index 77a3e2ea..cebc63fc 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
@@ -31,7 +31,6 @@ package at.gv.egiz.smcc;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.ResourceBundle;
-import java.util.logging.Logger;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
@@ -60,7 +59,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
this.resourceBundleName = resourceBundleName;
}
- String toString(byte[] b) {
+ protected String toString(byte[] b) {
StringBuffer sb = new StringBuffer();
if (b != null && b.length > 0) {
sb.append(Integer.toHexString((b[0] & 240) >> 4));
@@ -74,13 +73,46 @@ public abstract class AbstractSignatureCard implements SignatureCard {
return sb.toString();
}
- abstract byte[] selectFileAID(byte[] fid) throws CardException,
+ protected abstract byte[] selectFileAID(byte[] fid) throws CardException,
SignatureCardException;
- abstract byte[] selectFileFID(byte[] fid) throws CardException,
+ protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException,
SignatureCardException;
- byte[] readBinary(CardChannel channel, int offset, int len)
+ /**
+ * VERIFY PIN
+ *
+ * <p>
+ * Implementations of this method should call
+ * {@link PINProvider#providePIN(PINSpec, int)} to retrieve the PIN entered by
+ * the user and VERIFY PIN on the smart card until the PIN has been
+ * successfully verified.
+ * </p>
+ *
+ * @param pinProvider
+ * the PINProvider
+ * @param spec
+ * the PINSpec
+ * @param kid
+ * the key ID (KID) of the PIN to verify
+ *
+ * @throws CardException
+ * if smart card communication fails
+ *
+ * @throws CancelledException
+ * if the PINProvider indicated that the user canceled the PIN entry
+ * @throws NotActivatedException
+ * if the card application has not been activated
+ * @throws LockedException
+ * if the card application is locked
+ *
+ * @throws SignatureCardException
+ * if VERIFY PIN fails
+ */
+ protected abstract void verifyPIN(PINProvider pinProvider, PINSpec spec,
+ byte kid) throws CardException, SignatureCardException;
+
+ protected byte[] readBinary(CardChannel channel, int offset, int len)
throws CardException, SignatureCardException {
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0,
@@ -94,7 +126,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- int readBinary(int offset, int len, byte[] b) throws CardException,
+ protected int readBinary(int offset, int len, byte[] b) throws CardException,
SignatureCardException {
if (b.length < len) {
@@ -114,7 +146,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException,
+ protected byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException,
SignatureCardException {
CardChannel channel = getCardChannel();
@@ -150,15 +182,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid,
- int kfpc) throws CardException, SignatureCardException;
-
- public byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength)
+ /**
+ * Read the content of a TLV file.
+ *
+ * @param aid the application ID (AID)
+ * @param ef the elementary file (EF)
+ * @param maxLength the maximum length of the file
+ *
+ * @return the content of the file
+ *
+ * @throws SignatureCardException
+ */
+ protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength)
throws SignatureCardException {
return readTLVFilePIN(aid, ef, (byte) 0, null, null, maxLength);
}
- public byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid,
+
+ /**
+ * Read the content of a TLV file wich may require a PIN.
+ *
+ * @param aid the application ID (AID)
+ * @param ef the elementary file (EF)
+ * @param kid the key ID (KID) of the corresponding PIN
+ * @param provider the PINProvider
+ * @param spec the PINSpec
+ * @param maxLength the maximum length of the file
+ *
+ * @return the content of the file
+ *
+ * @throws SignatureCardException
+ */
+ protected byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid,
PINProvider provider, PINSpec spec, int maxLength)
throws SignatureCardException {
@@ -179,33 +234,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
// SELECT FILE (EF)
- rb = selectFileFID(ef);
- if (rb[rb.length - 2] != (byte) 0x90 || rb[rb.length - 1] != (byte) 0x00) {
+ ResponseAPDU resp = selectFileFID(ef);
+ if (resp.getSW() == 0x6a82) {
+
+ // EF not found
+ throw new FileNotFoundException("EF " + toString(ef) + " not found.");
+
+ } else if (resp.getSW() != 0x9000) {
throw new SignatureCardException("SELECT FILE with "
+ "FID="
+ toString(ef)
+ " failed ("
+ "SW="
- + Integer.toHexString((0xFF & (int) rb[rb.length - 1])
- | (0xFF & (int) rb[rb.length - 2]) << 8) + ").");
+ + Integer.toHexString(resp.getSW()) + ").");
+
}
// try to READ BINARY
- int sw = readBinary(0, 1, new byte[1]);
+ byte[] b = new byte[1];
+ int sw = readBinary(0, 1, b);
+
if (provider != null && sw == 0x6982) {
// VERIFY
- int kfpc = -1; // unknown
- while (true) {
- kfpc = verifyPIN(provider, spec, kid, kfpc);
- if (kfpc < -1) {
- return null;
- } else if (kfpc < 0) {
- break;
- }
+ verifyPIN(provider, spec, kid);
+
+ } else if (sw == 0x9000) {
+ // not expected type
+ if (b[0] != 0x30) {
+ throw new NotActivatedException();
}
- } else if (sw != 0x9000) {
+ } else {
throw new SignatureCardException("READ BINARY failed (SW="
+ Integer.toHexString(sw) + ").");
}
@@ -221,17 +281,56 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU)
+ /**
+ * Transmit the given command APDU using the given card channel.
+ *
+ * @param channel
+ * the card channel
+ * @param commandAPDU
+ * the command APDU
+ * @param logData
+ * <code>true</code> if command APDU data may be logged, or
+ * <code>false</code> otherwise
+ *
+ * @return the corresponding response APDU
+ *
+ * @throws CardException
+ * if smart card communication fails
+ */
+ protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU, boolean logData)
+ throws CardException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(commandAPDU
+ + (logData ? "\n" + toString(commandAPDU.getBytes()) : ""));
+ long t0 = System.currentTimeMillis();
+ ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
+ long t1 = System.currentTimeMillis();
+ log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] "
+ + (logData ? "\n" + toString(responseAPDU.getBytes()) : ""));
+ return responseAPDU;
+ } else {
+ return channel.transmit(commandAPDU);
+ }
+
+ }
+
+ /**
+ * Transmit the given command APDU using the given card channel.
+ *
+ * @param channel the card channel
+ * @param commandAPDU the command APDU
+ *
+ * @return the corresponding response APDU
+ *
+ * @throws CardException if smart card communication fails
+ */
+ protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU)
throws CardException {
- log.trace(commandAPDU + "\n" + toString(commandAPDU.getBytes()));
- long t0 = System.currentTimeMillis();
- ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
- long t1 = System.currentTimeMillis();
- log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] "
- + toString(responseAPDU.getBytes()));
- return responseAPDU;
+ return transmit(channel, commandAPDU, true);
}
+
public void init(Card card) {
card_ = card;
ATR atr = card.getATR();
@@ -242,7 +341,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
}
- CardChannel getCardChannel() {
+ protected CardChannel getCardChannel() {
return card_.getBasicChannel();
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java
new file mode 100644
index 00000000..f96611c2
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* 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 at.gv.egiz.smcc;
+
+public class FileNotFoundException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public FileNotFoundException() {
+ }
+
+ public FileNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FileNotFoundException(String message) {
+ super(message);
+ }
+
+ public FileNotFoundException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java
new file mode 100644
index 00000000..e00322a0
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java
@@ -0,0 +1,38 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* 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 at.gv.egiz.smcc;
+
+public class LockedException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public LockedException() {
+ }
+
+ public LockedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public LockedException(String message) {
+ super(message);
+ }
+
+ public LockedException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java
new file mode 100644
index 00000000..9181fc5f
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java
@@ -0,0 +1,44 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* 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 at.gv.egiz.smcc;
+
+/**
+ * This exception is thrown upon a call to a function that
+ * has not been activated (e.g. not yet activated citizen card).
+ */
+public class NotActivatedException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NotActivatedException() {
+ super();
+ }
+
+ public NotActivatedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotActivatedException(String message) {
+ super(message);
+ }
+
+ public NotActivatedException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
index 79e2663e..99acbc0f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
@@ -31,12 +31,21 @@ package at.gv.egiz.smcc;
import java.math.BigInteger;
import java.util.Arrays;
+import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
public class STARCOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(STARCOSCard.class);
public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 };
@@ -83,7 +92,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
public static final byte KID_PIN_SS = (byte) 0x81;
- // Gew�hnliche Signatur (GS)
+ // Gewöhnliche Signatur (GS)
public static final byte[] AID_DF_GS = new byte[] { (byte) 0xd0, (byte) 0x40,
(byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x13,
@@ -104,31 +113,157 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
// .
// )
(byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version
- (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID)
- (byte) 0x14 // ECDSA
+ (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID)
+ (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA
};
public static final byte KID_PIN_CARD = (byte) 0x01;
+ /**
+ * Creates an new instance.
+ */
public STARCOSCard() {
super("at/gv/egiz/smcc/STARCOSCard");
}
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName)
+ */
public byte[] getCertificate(KeyboxName keyboxName)
throws SignatureCardException {
+ byte[] aid;
+ byte[] efc;
if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
- return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000);
+ aid = AID_DF_SS;
+ efc = EF_C_X509_CH_DS;
} else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
- return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000);
+ aid = AID_DF_GS;
+ efc = EF_C_X509_CH_AUT;
} else {
throw new IllegalArgumentException("Keybox " + keyboxName
+ " not supported.");
}
+ log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" +
+ " (AID=" + toString(aid) + " EF=" + toString(efc) + ").");
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFile(aid, efc, 2000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String)
+ */
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
+ throws SignatureCardException {
+
+ if ("IdentityLink".equals(infobox)) {
+
+ PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD,
+ provider, spec, 2000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#createSignature(byte[], at.gv.egiz.smcc.SignatureCard.KeyboxName, at.gv.egiz.smcc.PINProvider)
+ */
+ public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
+ PINProvider provider) throws SignatureCardException {
+
+ if (hash.length != 20) {
+ throw new IllegalArgumentException("Hash value must be of length 20.");
+ }
+
+ byte[] aid;
+ byte kid;
+ byte[] dst;
+ PINSpec spec;
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
+ aid = AID_DF_SS;
+ kid = KID_PIN_SS;
+ dst = DST_SS;
+ spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"));
+
+ } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
+ aid = AID_DF_GS;
+ kid = KID_PIN_CARD;
+ dst = DST_GS;
+ spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
+
+ } else {
+ throw new IllegalArgumentException("KeyboxName '" + keyboxName
+ + "' not supported.");
+ }
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+
+ // SELECT MF
+ selectMF();
+ // SELECT DF
+ selectFileAID(aid);
+ // VERIFY
+ verifyPIN(provider, spec, kid);
+ // MSE: SET DST
+ mseSetDST(dst);
+ // PSO: HASH
+ psoHash(hash);
+ // PSO: COMPUTE DIGITAL SIGNATURE
+ return psoComputDigitalSiganture();
+
+
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
}
- byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
0x04, fid, 256));
@@ -150,16 +285,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
}
}
- byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02,
+ return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02,
0x04, fid, 256));
- if (resp.getSW() == 0x6a82) {
- throw new SignatureCardException("Failed to select file (FID="
- + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ".");
- } else {
- return resp.getBytes();
- }
}
void mseSetDST(byte[] dst) throws CardException, SignatureCardException {
@@ -201,134 +330,86 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
}
}
- int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc)
- throws CardException, SignatureCardException {
-
+ /**
+ * VERIFY PIN
+ * <p>
+ * If <code>pin</code> is <code>null</code> only the PIN status is checked and
+ * returned.
+ * </p>
+ *
+ * @param pin
+ * the PIN (may be <code>null</code>)
+ * @param kid
+ * the KID of the PIN to be verified
+ *
+ * @return -1 if VERIFY PIN was successful, or the number of possible retries
+ *
+ * @throws CardException
+ * if communication with the smart card fails.
+ * @throws NotActivatedException
+ * if the card application has not been activated
+ * @throws SignatureCardException
+ * if VERIFY PIN fails
+ */
+ private int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException {
+
CardChannel channel = getCardChannel();
- // get number of possible retries
- ResponseAPDU resp = transmit(channel,
- new CommandAPDU(0x00, 0x20, 0x00, kid));
- int retries;
- if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
- retries = resp.getSW2() & 0x0f;
- } else if (resp.getSW() == 0x6984) {
- // PIN LCS = "Initilized" (not activated)
- throw new SignatureCardException(spec.getLocalizedName() + " not set.");
- } else {
- throw new SignatureCardException("Failed to get PIN retries: SW="
- + Integer.toHexString(resp.getSW()));
- }
-
- // get PIN
- String pin = pinProvider.providePIN(spec, retries);
+ ResponseAPDU resp;
if (pin == null) {
- // User canceled operation
- // throw new CancelledException("User canceld PIN entry");
- return -2;
- }
- // PIN length in bytes
- int len = (int) Math.ceil(pin.length() / 2);
-
- // BCD encode PIN and marshal PIN block
- byte[] pinBytes = new BigInteger(pin, 16).toByteArray();
- byte[] pinBlock = new byte[8];
- if (len < pinBytes.length) {
- System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len);
+ resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid));
} else {
- System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1,
- pinBytes.length);
+ // PIN length in bytes
+ int len = (int) Math.ceil(pin.length() / 2);
+
+ // BCD encode PIN and marshal PIN block
+ byte[] pinBytes = new BigInteger(pin, 16).toByteArray();
+ byte[] pinBlock = new byte[8];
+ if (len < pinBytes.length) {
+ System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len);
+ } else {
+ System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1,
+ pinBytes.length);
+ }
+ pinBlock[0] = (byte) (0x20 + len * 2);
+ Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff);
+
+ resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false);
+
}
- pinBlock[0] = (byte) (0x20 + len * 2);
- Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff);
- resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock));
- if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ if (resp.getSW() == 0x63c0) {
+ throw new LockedException("PIN locked.");
+ } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ // return number of possible retries
return resp.getSW2() & 0x0f;
- } else if (resp.getSW() != 0x9000) {
+ } else if (resp.getSW() == 0x6984) {
+ // PIN LCS = "Initialized" (-> not activated)
+ throw new NotActivatedException("PIN not set.");
+ } else if (resp.getSW() == 0x9000) {
+ return -1; // success
+ } else {
throw new SignatureCardException("Failed to verify pin: SW="
+ Integer.toHexString(resp.getSW()));
- } else {
- return -1;
- }
-
- }
-
- public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
- PINProvider provider) throws SignatureCardException {
-
- if (hash.length != 20) {
- throw new IllegalArgumentException("Hash value must be of length 20");
- }
-
- byte[] aid;
- byte kid;
- byte[] dst;
- PINSpec spec;
- if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
- aid = AID_DF_SS;
- kid = KID_PIN_SS;
- dst = DST_SS;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"));
-
- } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
- aid = AID_DF_GS;
- kid = KID_PIN_CARD;
- dst = DST_GS;
- spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
-
- } else {
- throw new IllegalArgumentException("KeyboxName '" + keyboxName
- + "' not supported.");
}
-
- try {
-
- // SELECT MF
- selectMF();
- // SELECT DF
- selectFileAID(aid);
- // VERIFY
- int retr = -1; // unknown
- while (true) {
- retr = verifyPIN(provider, spec, kid, retr);
- if (retr < -1) {
- return null;
- } else if (retr < 0) {
- break;
- }
- }
- // MSE: SET DST
- mseSetDST(dst);
- // PSO: HASH
- psoHash(hash);
- // PSO: COMPUTE DIGITAL SIGNATURE
- byte[] rs = psoComputDigitalSiganture();
- return rs;
-
- } catch (CardException e) {
- throw new SignatureCardException("Failed to create signature.", e);
- }
-
+
}
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINProvider, at.gv.egiz.smcc.PINSpec, byte, int)
+ */
+ protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid)
+ throws CardException, SignatureCardException {
- public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
- throws SignatureCardException {
-
- if ("IdentityLink".equals(infobox)) {
-
- PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
- try {
- byte[] res = readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD,
- provider, spec, 2000);
- return res;
- } catch (Exception e) {
- throw new SignatureCardException(e);
+ int retries = verifyPIN(null, kid);
+ do {
+ String pin = pinProvider.providePIN(spec, retries);
+ if (pin == null) {
+ // user canceled operation
+ throw new CancelledException("User canceld operation.");
}
- } else {
- throw new IllegalArgumentException("Infobox '" + infobox
- + "' not supported.");
- }
+ retries = verifyPIN(pin, kid);
+ } while (retries > 0);
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
index 68a6f6df..22a66c3f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
@@ -25,6 +25,8 @@ import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
@@ -52,16 +54,20 @@ import org.apache.commons.logging.LogFactory;
*/
public class SWCard implements SignatureCard {
- private static final String BKU_USER_DIR = ".bku";
+ private static final String BKU_USER_DIR = ".mocca";
private static final String SWCARD_DIR = "smcc";
private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12";
+ private static final String KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR = "certified.pwd";
+
private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer";
private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12";
+ private static final String KEYSTORE_PASSWORD_SECURE_KEYPAIR = "secure.pwd";
+
private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer";
private static String swCardDir;
@@ -70,8 +76,12 @@ public class SWCard implements SignatureCard {
private KeyStore certifiedKeyStore;
+ private String certifiedKeyStorePassword;
+
private KeyStore secureKeyStore;
+ private String secureKeyStorePassword;
+
private Certificate certifiedCertificate;
private Certificate secureCertificate;
@@ -168,7 +178,7 @@ public class SWCard implements SignatureCard {
}
try {
- keyStore.load(keyStoreFile, null);
+ keyStore.load(keyStoreFile, password);
} catch (Exception e) {
String msg = "Failed to load KeyStore from file '" + fileName + "'.";
log.info(msg, e);
@@ -176,10 +186,33 @@ public class SWCard implements SignatureCard {
}
return keyStore;
-
}
+ private String loadKeyStorePassword(String passwordFileName) throws SignatureCardException {
+
+ String fileName = getFileName(passwordFileName);
+ FileInputStream keyStorePasswordFile;
+ try {
+ keyStorePasswordFile = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+
+ try {
+ InputStreamReader reader = new InputStreamReader(keyStorePasswordFile, Charset.forName("UTF-8"));
+ StringBuilder sb = new StringBuilder();
+ char b[] = new char[16];
+ for (int l; (l = reader.read(b)) != -1;) {
+ sb.append(b, 0, l);
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ throw new SignatureCardException("Failed to read file '" + passwordFileName + "'.");
+ }
+
+ }
+
private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException {
if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
@@ -198,6 +231,23 @@ public class SWCard implements SignatureCard {
}
+ private String getPassword(KeyboxName keyboxName) throws SignatureCardException {
+
+ if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ if (certifiedKeyStorePassword == null) {
+ certifiedKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR);
+ }
+ return certifiedKeyStorePassword;
+ } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ if (secureKeyStorePassword == null) {
+ secureKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_SECURE_KEYPAIR);
+ }
+ return secureKeyStorePassword;
+ } else {
+ throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported.");
+ }
+
+ }
public byte[] getCertificate(KeyboxName keyboxName)
throws SignatureCardException {
@@ -254,9 +304,21 @@ public class SWCard implements SignatureCard {
public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException {
// KeyStore password
- PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password");
-
- KeyStore keyStore = getKeyStore(keyboxName, null);
+ String password = getPassword(keyboxName);
+
+ if (password == null) {
+
+ PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password");
+
+ password = provider.providePIN(pinSpec, -1);
+
+ if (password == null) {
+ return null;
+ }
+
+ }
+
+ KeyStore keyStore = getKeyStore(keyboxName, password.toCharArray());
PrivateKey privateKey = null;
@@ -269,8 +331,7 @@ public class SWCard implements SignatureCard {
Key key = null;
while (key == null) {
try {
- String pin = provider.providePIN(pinSpec, -1);
- key = keyStore.getKey(alias, pin.toCharArray());
+ key = keyStore.getKey(alias, password.toCharArray());
} catch (UnrecoverableKeyException e) {
log.info("Failed to get Key from KeyStore. Wrong password?", e);
}
@@ -315,8 +376,6 @@ public class SWCard implements SignatureCard {
@Override
public void setLocale(Locale locale) {
- // TODO Auto-generated method stub
- throw new UnsupportedOperationException("Not supported yet.");
}
@Override
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
index f2a964fe..f296f3a2 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
@@ -34,7 +34,7 @@ public class SignatureCardException extends Exception {
*
*/
private static final long serialVersionUID = 1L;
-
+
/**
* Creates a new instance of this <code>SignatureCardException</code>.
*
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
index 2131a737..777299d9 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -28,19 +28,189 @@
//
package at.gv.egiz.smcc;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A factory for creating {@link SignatureCard}s from {@link Card}s.
+ */
public class SignatureCardFactory {
+
+ /**
+ * This class represents a supported smart card.
+ */
+ private class SupportedCard {
+
+ /**
+ * The ATR pattern.
+ */
+ private byte[] atrPattern;
+
+ /**
+ * The ATR mask.
+ */
+ private byte[] atrMask;
+
+ /**
+ * The implementation class.
+ */
+ private String impl;
- public static SignatureCardFactory getInstance() {
- return new SignatureCardFactory();
+ /**
+ * Creates a new SupportedCard instance with the given ATR pattern and mask
+ * und the corresponding implementation class.
+ *
+ * @param atrPattern
+ * the ATR pattern
+ * @param atrMask
+ * the ATR mask
+ * @param implementationClass
+ * the name of the implementation class
+ *
+ * @throws NullPointerException
+ * if <code>atrPattern</code> or <code>atrMask</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * if the lengths of <code>atrPattern</code> and
+ * <code>atrMask</code> of not equal.
+ */
+ public SupportedCard(byte[] atrPattern, byte[] atrMask, String implementationClass) {
+ if (atrPattern.length != atrMask.length) {
+ throw new IllegalArgumentException("Length of 'atr' and 'mask' must be equal.");
+ }
+ this.atrPattern = atrPattern;
+ this.atrMask = atrMask;
+ this.impl = implementationClass;
+ }
+
+ /**
+ * Returns true if the given ATR matches the ATR pattern and mask this
+ * SupportedCard object.
+ *
+ * @param atr
+ * the ATR
+ *
+ * @return <code>true</code> if the given ATR matches the ATR pattern and
+ * mask of this SupportedCard object, or <code>false</code>
+ * otherwise.
+ */
+ public boolean matches(ATR atr) {
+
+ byte[] bytes = atr.getBytes();
+ if (bytes == null) {
+ return false;
+ }
+ if (bytes.length < atrMask.length) {
+ // we cannot test for equal length here, as we get ATRs with
+ // additional bytes on systems using PCSClite (e.g. linux and OS X) sometimes
+ return false;
+ }
+
+ int l = Math.min(atrMask.length, bytes.length);
+ for (int i = 0; i < l; i++) {
+ if ((bytes[i] & atrMask[i]) != atrPattern[i]) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * @return the corresponding implementation class.
+ */
+ public String getImplementationClassName() {
+ return impl;
+ }
+
+ }
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(SignatureCardFactory.class);
+
+ /**
+ * The instance to be returned by {@link #getInstance()}.
+ */
+ private static SignatureCardFactory instance;
+
+ /**
+ * The list of supported smart cards.
+ */
+ private List<SupportedCard> supportedCards;
+
+ /**
+ * @return an instance of this SignatureCardFactory.
+ */
+ public static synchronized SignatureCardFactory getInstance() {
+ if (instance == null) {
+ instance = new SignatureCardFactory();
+ }
+ return instance;
}
+ /**
+ * Private constructor.
+ */
private SignatureCardFactory() {
+
+ supportedCards = new ArrayList<SupportedCard>();
+
+ // e-card
+ supportedCards.add(new SupportedCard(
+ // ATR (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0x3b, (byte) 0xbd, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,
+ (byte) 0x80, (byte) 0x51, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ "at.gv.egiz.smcc.STARCOSCard"));
+ // a-sign premium
+ supportedCards.add(new SupportedCard(
+ // ATR (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,
+ (byte) 0x45, (byte) 0x50, (byte) 0x41, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ "at.gv.egiz.smcc.ACOSCard"));
+
}
+ /**
+ * Creates a SignatureCard instance with the given smart card.
+ *
+ * @param card
+ * the smart card, or <code>null</code> if a software card should be
+ * created
+ *
+ * @return a SignatureCard instance
+ *
+ * @throws CardNotSupportedException
+ * if no implementation of the given <code>card</code> could be
+ * found
+ */
public SignatureCard createSignatureCard(Card card)
throws CardNotSupportedException {
@@ -51,31 +221,34 @@ public class SignatureCardFactory {
}
ATR atr = card.getATR();
- byte[] historicalBytes = atr.getHistoricalBytes();
- if(historicalBytes == null || historicalBytes.length < 3) {
- throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
+ Iterator<SupportedCard> cards = supportedCards.iterator();
+ while (cards.hasNext()) {
+ SupportedCard supportedCard = cards.next();
+ if(supportedCard.matches(atr)) {
+
+ ClassLoader cl = SignatureCardFactory.class.getClassLoader();
+ SignatureCard sc;
+ try {
+ Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName());
+ sc = (SignatureCard) scClass.newInstance();
+ sc.init(card);
+ return sc;
+
+ } catch (ClassNotFoundException e) {
+ log.warn("Cannot find signature card implementation class.", e);
+ throw new CardNotSupportedException("Cannot find signature card implementation class.", e);
+ } catch (InstantiationException e) {
+ log.warn("Failed to instantiate signature card implementation.", e);
+ throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
+ } catch (IllegalAccessException e) {
+ log.warn("Failed to instantiate signature card implementation.", e);
+ throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
+ }
+
+ }
}
- int t = ((0xFF & (int) historicalBytes[0]) << 16) +
- ((0xFF & (int) historicalBytes[1]) << 8) +
- (0xFF & (int) historicalBytes[2]);
-
- SignatureCard sCard;
- switch (t) {
- case 0x455041 :
- case 0x4D4341 :
- sCard = new ACOSCard();
- break;
-
- case 0x805102 :
- sCard = new STARCOSCard();
- break;
-
- default :
- throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
- }
- sCard.init(card);
- return sCard;
+ throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
index ffffd3af..b70b44a7 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
@@ -142,7 +142,8 @@ public class SmartCardIO {
for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
Card card = null;
- try {
+ try {
+ log.trace("Trying to connect to card.");
// try to connect to card
card = terminal.connect("*");
} catch (CardException e) {
diff --git a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java
new file mode 100644
index 00000000..b921a5d5
--- /dev/null
+++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java
@@ -0,0 +1,92 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* 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 at.gv.egiz.smcc;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import at.gv.egiz.smcc.SignatureCard.KeyboxName;
+import at.gv.egiz.smcc.util.SMCCHelper;
+
+public class STARCOSCardTest {
+
+ /**
+ * @param args
+ * @throws CardException
+ * @throws NoSuchAlgorithmException
+ */
+ public static void main(String[] args) throws CardException, NoSuchAlgorithmException {
+
+ SMCCHelper helper = new SMCCHelper();
+ while (helper.getResultCode() != SMCCHelper.CARD_FOUND) {
+ System.out.println("Did not get a signature card ... " + helper.getResultCode());
+ helper.update();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault());
+
+ System.out.println("Found '" + signatureCard + "'.");
+
+ try {
+// signatureCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR);
+// signatureCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR);
+// signatureCard.getInfobox("IdentityLink", new CommandLinePINProvider(), null);
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+ byte[] digest = messageDigest.digest("test".getBytes());
+ signatureCard.createSignature(digest, KeyboxName.CERITIFIED_KEYPAIR, new CommandLinePINProvider());
+ } catch (SignatureCardException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private static class CommandLinePINProvider implements PINProvider {
+
+ @Override
+ public String providePIN(PINSpec spec, int retries) {
+
+ InputStreamReader inputStreamReader = new InputStreamReader(System.in);
+ BufferedReader in = new BufferedReader(inputStreamReader);
+
+ System.out.print("Enter " + spec.getLocalizedName() + " ["
+ + spec.getMinLength() + "-" + spec.getMaxLength() + "] (" + retries
+ + " retries):");
+
+ try {
+ return in.readLine();
+ } catch (IOException e) {
+ return null;
+ }
+
+ }
+
+ }
+
+}
diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java
index b48d6d50..cfac8cf5 100644
--- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java
+++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java
@@ -20,6 +20,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.gv.egiz.smcc.CancelledException;
+import at.gv.egiz.smcc.LockedException;
+import at.gv.egiz.smcc.NotActivatedException;
import at.gv.egiz.smcc.PINProvider;
import at.gv.egiz.smcc.PINSpec;
import at.gv.egiz.smcc.SignatureCard;
@@ -100,6 +102,12 @@ public class InfoBoxReadRequestHandler extends AbstractRequestHandler implements
stalResp.setInfoboxValue(resp);
return stalResp;
}
+ } catch (NotActivatedException e) {
+ log.info("Citizen card not activated.", e);
+ return new ErrorResponse(6001);
+ } catch (LockedException e) {
+ log.info("Citizen card locked.", e);
+ return new ErrorResponse(6001);
} catch (CancelledException cx) {
log.debug("User cancelled request", cx);
return new ErrorResponse(6001);
diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java
index dcd12b02..466ec2a9 100644
--- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java
+++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java
@@ -32,6 +32,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.gv.egiz.smcc.CancelledException;
+import at.gv.egiz.smcc.LockedException;
+import at.gv.egiz.smcc.NotActivatedException;
import at.gv.egiz.smcc.PINProvider;
import at.gv.egiz.smcc.PINSpec;
import at.gv.egiz.smcc.SignatureCard;
@@ -78,7 +80,7 @@ public abstract class SignRequestHandler extends AbstractRequestHandler implemen
String jceName = JCEAlgorithmNames.getJCEHashName(signatureMethod);
if (jceName == null) {
log.error("Hash algorithm not supported:");
- return new ErrorResponse(1000);
+ return new ErrorResponse(4006);
}
MessageDigest md = MessageDigest.getInstance(jceName);
md.update(signReq.getSignedInfo());
@@ -90,6 +92,12 @@ public abstract class SignRequestHandler extends AbstractRequestHandler implemen
SignResponse stalResp = new SignResponse();
stalResp.setSignatureValue(resp);
return stalResp;
+ } catch (NotActivatedException e) {
+ log.info("Citizen card not activated.", e);
+ return new ErrorResponse(6001);
+ } catch (LockedException e) {
+ log.info("Citizen card locked.", e);
+ return new ErrorResponse(6001);
} catch (CancelledException cx) {
log.debug("User cancelled request");
return new ErrorResponse(6001);