summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java277
1 files changed, 249 insertions, 28 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
index 768ac959..ccb463a5 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
@@ -1,5 +1,25 @@
+/*
+* Copyright 2009 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 iaik.me.asn1.ASN1;
+
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
@@ -25,6 +45,9 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
(byte) 0x72, (byte) 0x2E, (byte) 0x46, (byte) 0x69, (byte) 0x6C,
(byte) 0x65 };
+ private final String SIG_KEY_NAME = "KprivFirmaDigital";
+ private final String SIG_CERT_NAME = "CertFirmaDigital";
+
protected PinInfo pinInfo = new PinInfo(8, 16,
"[0-9A-Za-z_<>!()?%\\-=&+\\.]", "at/gv/egiz/smcc/DNIeCard",
"sig.pin", (byte) 0x00, new byte[] {}, PinInfo.UNKNOWN_RETRIES);
@@ -52,9 +75,13 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
}
try {
+
+ byte[] prKdf = executeReadPrKDF(channel);
+ byte[] keyId = getKeyIdFromASN1File(prKdf);
+
verifyPINLoop(channel, pinInfo, pinGUI);
- secureChannel.executeSecureManageSecurityEnvironment(channel);
+ secureChannel.executeSecureManageSecurityEnvironment(channel, keyId);
MessageDigest md;
try {
@@ -88,14 +115,30 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
CardChannel channel = getCardChannel();
- if (!secureChannel.isEstablished())
+ if (!secureChannel.isEstablished()) {
try {
secureChannel.establish(channel);
} catch (CardException e) {
log.debug("Error establishing secure channel to card.", e);
}
+ }
+ byte[] certId = null;
+
+ try {
+ // read CDF file
+ byte[] cdf = executeReadCDF(channel);
+
+ // extract certificate id from ASN1 data structure
+ certId = getCertIdFromASN1File(cdf);
+
+ } catch (CardException e1) {
+
+ log.error("Error reading ASN.1 data!");
+ e1.printStackTrace();
+ }
+
log.debug("Try to read certificate..");
try {
@@ -112,11 +155,11 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
secureChannel.executeSecureSelect(channel, apdu);
- // select 7005
+ // select cert id
byte[] apdu2 = new byte[] {
(byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02,
- (byte) 0x70, (byte) 0x05 };
+ certId[certId.length-2], certId[certId.length-1] };
byte[] fci = secureChannel.executeSecureSelect(channel, apdu2);
@@ -126,14 +169,11 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
byte[] data = secureChannel.executeSecureReadBinary(channel,
sizeHi, sizeLo);
- int uncompressedDataLen = getUncompressedDataLength(data);
-
byte[] compressedWithoutHeader = new byte[data.length - 8];
System.arraycopy(data, 8, compressedWithoutHeader, 0,
compressedWithoutHeader.length);
- result = decompressData(compressedWithoutHeader,
- uncompressedDataLen);
+ result = decompressData(compressedWithoutHeader);
} catch (CardException e) {
@@ -218,37 +258,218 @@ public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
secureChannel.executeSecureSelect(channel, apdu);
}
- private int getUncompressedDataLength(byte[] data) {
-
- byte len0 = data[0];
- byte len1 = data[1];
- byte len2 = data[2];
- byte len3 = data[3];
+ private byte[] executeReadCDF(CardChannel channel) throws CardException {
+
+ return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x04});
+ }
+
+ private byte[] executeReadPrKDF(CardChannel channel) throws CardException {
+
+ return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x01});
+ }
+
+ private byte[] getKeyIdFromASN1File(byte[] file) throws CardException {
+
+ // split file in two records
+ int record1Length = getRecordLength(file, 1);
+
+ byte[] record1 = new byte[record1Length];
+ byte[] record2 = new byte[file.length - record1.length];
+
+ System.arraycopy(file, 0, record1, 0, record1.length);
+ System.arraycopy(file, record1.length, record2, 0, record2.length);
+
+ byte[] keyId = new byte[2];
+
+ try {
+ ASN1 asn1_1 = new ASN1(record1);
+ ASN1 asn1_2 = new ASN1(record2);
+
+ if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) {
+
+ byte[] data = asn1_1.getElementAt(2).gvByteArray();
+
+ keyId[0] = data[9];
+ keyId[1] = data[10];
+ }
- int a = len0;
- int b = len1 * 256;
- int c = len2 * 256 * 256;
- int d = len3 * 256 * 256 * 256;
+ else if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) {
+
+ byte[] data = asn1_2.getElementAt(2).gvByteArray();
+
+ keyId[0] = data[9];
+ keyId[1] = data[10];
+ }
+
+ } catch (Exception e) {
- return a + b + c + d;
+ throw new CardException("Error getting ASN1 data.", e);
+ }
+
+ return keyId;
}
-
- private byte[] decompressData(byte[] input, int len) throws CardException {
+
+ private byte[] getCertIdFromASN1File(byte[] file) throws CardException {
+
+ int record1Length = getRecordLength(file, 1);
+
+ // split file in two records
+ byte[] record1 = new byte[record1Length];
+ byte[] record2 = new byte[file.length - record1.length];
+
+ System.arraycopy(file, 0, record1, 0, record1.length);
+ System.arraycopy(file, record1.length, record2, 0, record2.length);
+
+ byte[] certId = null;
+
+ try {
+ ASN1 asn1_1 = new ASN1(record1);
+ ASN1 asn1_2 = new ASN1(record2);
+
+ if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) {
+
+ certId = retrieveCertId(asn1_1.getElementAt(2).gvByteArray());
+ }
+
+ if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) {
+
+ certId = retrieveCertId(asn1_2.getElementAt(2).gvByteArray());
+ }
+
+ } catch (Exception e) {
+
+ throw new CardException("Error getting ASN1 data.", e);
+ }
+
+ return certId;
+ }
+
+ private byte[] retrieveCertId(byte[] data) throws CardException {
+
+ ASN1 contextSpecific = getASN1WithinContextSpecific(data);
+ try {
+ return contextSpecific.getElementAt(0).getElementAt(0).gvByteArray();
+ } catch (IOException e) {
+ throw new CardException("Error retrieving certificate ID from ASN1 data.", e);
+ }
+ }
+
+ private ASN1 getASN1WithinContextSpecific(byte[] data) throws CardException {
+
+ byte first = data[0];
+ byte lengthOfLength = 0;
+
+ if(first < 0) {
+
+ lengthOfLength = (byte)(first & (byte)0x7F);
+ lengthOfLength = (byte)(lengthOfLength +1);
+ } else {
+
+ lengthOfLength = 1;
+ }
+
+ byte[] asn1data = new byte[data.length - lengthOfLength];
+ System.arraycopy(data, lengthOfLength, asn1data, 0, asn1data.length);
+
+ try {
+ return new ASN1(asn1data);
+ } catch (IOException e) {
+ throw new CardException("Error getting ASN1 structure.", e);
+ }
+ }
+
+ private int getRecordLength(byte[] data, int startOfLength) {
+
+ byte lengthStartByte = data[startOfLength];
+
+ if(lengthStartByte < 0) {
+ // we have more than one length byte
+ byte lengthOfLength = (byte)(lengthStartByte & (byte)0x7F);
+
+ byte[] lengthValues = new byte[lengthOfLength];
+ System.arraycopy(data, startOfLength+1, lengthValues, 0, lengthOfLength);
+
+ int result = 0;
+
+ for(int i=0; i<lengthValues.length; i++) {
+
+ result = (result + byteToInt(lengthValues[lengthValues.length-1-i]) * (int)Math.pow(256, i));
+ }
+
+ return result + startOfLength + lengthOfLength + 1; // defined length + tag byte + length bytes
+
+ } else {
+
+ return (int)lengthStartByte + startOfLength + 1; // defined length + tag byte + length byte
+ }
+
+ }
+
+ private int byteToInt(byte b) {
+
+ return b < 0 ? b + 256 : b;
+
+ }
+
+ private byte[] executeReadFile(CardChannel channel, byte[] path) throws CardException {
+
+ log.debug("Executing secure read File command..");
+
+ executeSecureSelectMasterFile(channel);
+
+ // Select DF
+ byte[] apdu_1 = new byte[] {
+
+ (byte)0x00, // CLA
+ (byte)0xA4, // INS
+ (byte)0x00, // P1
+ (byte)0x00, // P2
+ (byte)0x02, // Lc
+ path[0],
+ path[1]
+ };
+
+ secureChannel.executeSecureSelect(channel, apdu_1);
+
+ // Select EF
+ byte[] apdu_2 = new byte[] {
+
+ (byte)0x00, // CLA
+ (byte)0xA4, // INS
+ (byte)0x00, // P1
+ (byte)0x00, // P2
+ (byte)0x02, // Lc
+ path[2],
+ path[3]
+ };
+
+ byte[] fci = secureChannel.executeSecureSelect(channel, apdu_2);
+ byte[] file = secureChannel.executeSecureReadBinary(channel, fci[7], fci[8]);
+
+ return file;
+ }
+
+ private byte[] decompressData(byte[] input) throws CardException {
Inflater decompresser = new Inflater();
decompresser.setInput(input, 0, input.length);
- byte[] result = new byte[len];
-
+ byte[] buffer = new byte[256];
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
try {
- decompresser.inflate(result);
- decompresser.end();
-
- return result;
+ while(!decompresser.finished()) {
+
+ int numBytes = decompresser.inflate(buffer);
+ bos.write(buffer, 0, numBytes);
+ }
+ decompresser.end();
+
} catch (DataFormatException e) {
throw new CardException("Error decompressing file.", e);
}
- }
+ return bos.toByteArray();
+ }
}