/* * eID Applet Project. * Copyright (C) 2008-2009 FedICT. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package be.fedict.eid.applet.shared; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; /** * Authentication Contract class. * * <p> * Volgens artikel 4, paragraaf 5 van de wet van 9 juli 2001 betreffende het * juridisch kader voor elektronische handtekeningen en certificatiediensten kan * de rechtsgeldigheid van elektronische handtekeningen niet worden ontzegd op * grond van het feit dat de handtekening niet is gebaseerd op een * gekwalificeerd certificaat. Hieruit kan men afleiden dat cryptografische * handtekeningen, gemaakt met het authenticatie certificaat, tevens als * rechtsgeldige elektronische handtekeningen kunnen worden geïnterpreteerd. * </p> * * <p> * This class allows a citizen to proof that the cryptographic signature created * with his authentication certificate was indeed meant as way of * authenticating. In case of challenge abuse, there was no intention to sign * any legally binding contract except this authentication contract. If a * malicious eID Applet Service tricks the citizen into signing a document * digest instead of a meaningless challenge, we give the citizen a proof of * intention via this formal authentication contract. * </p> * * <p> * We use a 32-bit TLV structure to prevent type flaw attacks. * </p> * * @author Frank Cornelis * */ public class AuthenticationContract { public static final int SALT_TAG = 1; public static final int HOSTNAME_TAG = 2; public static final int INET_ADDRESS_TAG = 3; public static final int LEGAL_NOTICE_TAG = 4; public static final int SESSION_ID_TAG = 5; public static final int ENCODED_SERVER_CERTIFICATE_TAG = 6; public static final int CHALLENGE_TAG = 7; private final byte[] salt; private final String hostname; private final InetAddress inetAddress; public static final String LEGAL_NOTICE = "Declaration of authentication intension.\n" + "The following data should be interpreted as an authentication challenge.\n"; private final byte[] sessionId; private final byte[] encodedServerCertificate; private final byte[] challenge; /** * Main constructor. * * @param salt * @param hostname * the optional hostname. * @param inetAddress * the optional internet address. * @param sessionId * the optional SSL session identifier. * @param encodedServerCertificate * the optional DER encoded X509 server certificate. * @param challenge */ public AuthenticationContract(byte[] salt, String hostname, InetAddress inetAddress, byte[] sessionId, byte[] encodedServerCertificate, byte[] challenge) { this.salt = salt; this.hostname = hostname; this.inetAddress = inetAddress; this.sessionId = sessionId; this.encodedServerCertificate = encodedServerCertificate; this.challenge = challenge; } public byte[] calculateToBeSigned() throws IOException { ByteArrayOutputStream toBeSignedOutputStream = new ByteArrayOutputStream(); /* * Salting prevents that we sign a document digest directly instead of * some meaningless challenge. */ writeTag(SALT_TAG, this.salt, toBeSignedOutputStream); if (null != this.hostname) { /* * Signing (salt||hostname||challenge) prevents man-in-the-middle * attacks from websites for which the SSL certificate is still * trusted but that have been compromised. If at the same time the * DNS is also attacked, well then everything is lost anyway. */ writeTag(HOSTNAME_TAG, this.hostname.getBytes(), toBeSignedOutputStream); } if (null != this.inetAddress) { byte[] address = this.inetAddress.getAddress(); writeTag(INET_ADDRESS_TAG, address, toBeSignedOutputStream); } /* * Next is to prevent abuse of the challenge in the context of a digital * signature claim on this cryptographic authentication signature. */ writeTag(LEGAL_NOTICE_TAG, LEGAL_NOTICE.getBytes(), toBeSignedOutputStream); if (null != this.sessionId) { writeTag(SESSION_ID_TAG, this.sessionId, toBeSignedOutputStream); } if (null != this.encodedServerCertificate) { writeTag(ENCODED_SERVER_CERTIFICATE_TAG, this.encodedServerCertificate, toBeSignedOutputStream); } /* * Of course we also digest the challenge as the server needs some mean * to authenticate us. */ writeTag(CHALLENGE_TAG, this.challenge, toBeSignedOutputStream); byte[] toBeSigned = toBeSignedOutputStream.toByteArray(); return toBeSigned; } /** * Write a 32-bit TLV entry to the given output stream. * * @param tag * @param value * @param outputStream * @throws IOException */ private void writeTag(int tag, byte[] value, OutputStream outputStream) throws IOException { outputStream.write(intToByteArray(tag)); if (null == value) { outputStream.write(intToByteArray(0)); } else { outputStream.write(intToByteArray(value.length)); outputStream.write(value); } } private byte[] intToByteArray(int value) { return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value }; } }