Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JMRTD - A Java API for accessing machine readable travel documents.
*
* Copyright (C) 2006 - 2017 The JMRTD team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id: TAProtocol.java 1662 2017-03-20 19:13:03Z martijno $
*/
package org.jmrtd.protocol;
import java.io.ByteArrayOutputStream;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.List;
import java.util.logging.Logger;
import org.jmrtd.PassportService;
import org.jmrtd.Util;
import org.jmrtd.cert.CVCAuthorizationTemplate.Role;
import org.jmrtd.cert.CVCPrincipal;
import org.jmrtd.cert.CardVerifiableCertificate;
import org.jmrtd.lds.icao.MRZInfo;
import net.sf.scuba.smartcards.CardServiceException;
import net.sf.scuba.tlv.TLVOutputStream;
/**
* The EAC Terminal Authentication protocol.
*
* @author The JMRTD team ([email protected])
*
* @version $Revision: 1662 $
*
* @since 0.5.6
*/
public class TAProtocol {
private static final Logger LOGGER = Logger.getLogger("org.jmrtd");
private static final int TAG_CVCERTIFICATE_SIGNATURE = 0x5F37;
private PassportService service;
private SecureMessagingWrapper wrapper;
public TAProtocol(PassportService service, SecureMessagingWrapper wrapper) {
this.service = service;
this.wrapper = wrapper;
}
/* From BSI-03110 v1.1, B.2:
*
*
* The following sequence of commands SHALL be used to implement Terminal Authentication:
* 1. MSE:Set DST
* 2. PSO:Verify Certificate
* 3. MSE:Set AT
* 4. Get Challenge
* 5. External Authenticate
* Steps 1 and 2 are repeated for every CV certificate to be verified
* (CVCA Link Certificates, DV Certificate, IS Certificate).
*
*/
/**
* Perform TA (Terminal Authentication) part of EAC (version 1). For details see
* TR-03110 ver. 1.11. In short, we feed the sequence of terminal
* certificates to the card for verification, get a challenge from the
* card, sign it with terminal private key, and send back to the card
* for verification.
*
* @param caReference reference issuer
* @param terminalCertificates terminal certificate chain
* @param terminalKey terminal private key
* @param taAlg algorithm
* @param chipAuthenticationResult the chip authentication result
* @param documentNumber the document number
*
* @return the challenge from the card
*
* @throws CardServiceException on error
*/
public synchronized TAResult doTA(CVCPrincipal caReference, List terminalCertificates,
PrivateKey terminalKey, String taAlg, CAResult chipAuthenticationResult, String documentNumber) throws CardServiceException {
try {
if (terminalCertificates == null || terminalCertificates.isEmpty()) {
throw new IllegalArgumentException("Need at least 1 certificate to perform TA, found: " + terminalCertificates);
}
byte[] caKeyHash = chipAuthenticationResult.getKeyHash();
/* The key hash that resulted from CA. */
if (caKeyHash == null) {
throw new IllegalArgumentException("CA key hash is null");
}
/* FIXME: check that terminalCertificates holds a (inverted, i.e. issuer before subject) chain. */
/* Check if first cert is/has the expected CVCA, and remove it from chain if it is the CVCA. */
CardVerifiableCertificate firstCert = terminalCertificates.get(0);
Role firstCertRole = firstCert.getAuthorizationTemplate().getRole();
if (Role.CVCA.equals(firstCertRole)) {
CVCPrincipal firstCertHolderReference = firstCert.getHolderReference();
if (caReference != null && !caReference.equals(firstCertHolderReference)) {
throw new CardServiceException("First certificate holds wrong authority, found \"" + firstCertHolderReference.getName() + "\", expected \"" + caReference.getName() + "\"");
}
if (caReference == null) {
caReference = firstCertHolderReference;
}
terminalCertificates.remove(0);
}
CVCPrincipal firstCertAuthorityReference = firstCert.getAuthorityReference();
if (caReference != null && !caReference.equals(firstCertAuthorityReference)) {
throw new CardServiceException("First certificate not signed by expected CA, found " + firstCertAuthorityReference.getName() + ", expected " + caReference.getName());
}
if (caReference == null) {
caReference = firstCertAuthorityReference;
}
/* Check if the last cert is an IS cert. */
CardVerifiableCertificate lastCert = terminalCertificates.get(terminalCertificates.size() - 1);
Role lastCertRole = lastCert.getAuthorizationTemplate().getRole();
if (!Role.IS.equals(lastCertRole)) {
throw new CardServiceException("Last certificate in chain (" + lastCert.getHolderReference().getName() + ") does not have role IS, but has role " + lastCertRole);
}
CardVerifiableCertificate terminalCert = lastCert;
/* Have the MRTD check our chain. */
for (CardVerifiableCertificate cert: terminalCertificates) {
try {
CVCPrincipal authorityReference = cert.getAuthorityReference();
/* Step 1: MSE:SetDST */
/* Manage Security Environment: Set for verification: Digital Signature Template,
* indicate authority of cert to check.
*/
byte[] authorityRefBytes = Util.wrapDO((byte) 0x83, authorityReference.getName().getBytes("ISO-8859-1"));
service.sendMSESetDST(wrapper, authorityRefBytes);
/* Cert body is already in TLV format. */
byte[] body = cert.getCertBodyData();
/* Signature not yet in TLV format, prefix it with tag and length. */
byte[] signature = cert.getSignature();
ByteArrayOutputStream sigOut = new ByteArrayOutputStream();
TLVOutputStream tlvSigOut = new TLVOutputStream(sigOut);
tlvSigOut.writeTag(TAG_CVCERTIFICATE_SIGNATURE);
tlvSigOut.writeValue(signature);
tlvSigOut.close();
signature = sigOut.toByteArray();
/* Step 2: PSO:Verify Certificate */
service.sendPSOExtendedLengthMode(wrapper, body, signature);
} catch (CardServiceException cse) {
throw cse;
} catch (Exception e) {
/* FIXME: Does this mean we failed to authenticate? -- MO */
throw new CardServiceException(e.getMessage());
}
}
if (terminalKey == null) {
throw new CardServiceException("No terminal key");
}
/* Step 3: MSE Set AT */
CVCPrincipal holderRef = terminalCert.getHolderReference();
byte[] holderRefBytes = Util.wrapDO((byte) 0x83, holderRef.getName().getBytes("ISO-8859-1"));
/* Manage Security Environment: Set for external authentication: Authentication Template */
service.sendMSESetATExtAuth(wrapper, holderRefBytes);
/* Step 4: send get challenge */
byte[] rPICC = service.sendGetChallenge(wrapper);
/* Step 5: external authenticate. */
/* FIXME: idPICC should be public key in case of PACE. See BSI TR 03110 v2.03 4.4. */
byte[] idPICC = new byte[documentNumber.length() + 1];
System.arraycopy(documentNumber.getBytes("ISO-8859-1"), 0, idPICC, 0, documentNumber.length());
idPICC[idPICC.length - 1] = (byte)MRZInfo.checkDigit(documentNumber);
ByteArrayOutputStream dtbs = new ByteArrayOutputStream();
dtbs.write(idPICC);
dtbs.write(rPICC);
dtbs.write(caKeyHash);
dtbs.close();
byte[] dtbsBytes = dtbs.toByteArray();
String sigAlg = terminalCert.getSigAlgName();
if (sigAlg == null) {
throw new IllegalStateException("ERROR: Could not determine signature algorithm for terminal certificate " + terminalCert.getHolderReference().getName());
}
Signature sig = Signature.getInstance(sigAlg);
sig.initSign(terminalKey);
sig.update(dtbsBytes);
byte[] signedData = sig.sign();
if (sigAlg.toUpperCase().endsWith("ECDSA")) {
int keySize = ((org.bouncycastle.jce.interfaces.ECPrivateKey)terminalKey).getParameters().getCurve().getFieldSize() / 8;
signedData = Util.getRawECDSASignature(signedData, keySize);
}
service.sendMutualAuthenticate(wrapper, signedData);
return new TAResult(chipAuthenticationResult, caReference, terminalCertificates, terminalKey, documentNumber, rPICC);
} catch (CardServiceException cse) {
throw cse;
} catch (Exception e) {
throw new CardServiceException(e.toString());
}
}
}