/*
* The MIT License
*
* Copyright 2014 Rui Martinho (rmartinho@gmail.com), António Braz (antoniocbraz@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.poreid.verify.sod;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.poreid.verify.util.Util;
/**
*
* @author POReID
*/
public class Validator {
private final String BI = "bi";
private final KeyStore keystore;
private X509Certificate certificate;
private SOD sod;
private CitizenIdentificationAttributes id;
private CitizenAddressAttributes address;
private CitizenPhotoAttributes photo;
private UUID uuid;
private byte[] signatureBytes;
public Validator(KeyStore keystore){
this.keystore = keystore;
}
public void setSOD(byte[] sod) throws ValidatorException{
try {
this.sod = (null != sod) ? new SOD(Arrays.copyOfRange(sod, 4, sod.length), keystore) : null;
} catch (SODException ex) {
throw new ValidatorException("Formato inválido - SOD", ex);
}
}
public void setID(byte[] id){
this.id = (null != id) ? new CitizenIdentificationAttributes(id) : null;
}
public void setAddress(byte[] address){
this.address = (null != address) ? new CitizenAddressAttributes(address) : null;
}
public void setPhoto(byte[] photo){
this.photo = (null != photo) ? new CitizenPhotoAttributes(photo) : null;
}
public void validate() throws ValidatorException{
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
if (null != id) {
md.update(id.getRawData());
}
if (null != address) {
md.update(address.getRawData());
}
if (null != photo) {
md.update(photo.getRawData());
}
md.update(uuid.toString().getBytes());
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(certificate);
sig.update(md.digest());
if (!sig.verify(signatureBytes)){
throw new ValidatorException("Não foi possivel validar os dados enviados (assinatura)");
}
if (!Util.isLeafCertificateValid(keystore, certificate)){
throw new ValidatorException("Não foi possivel validar os dados enviados (certificado)");
}
if (null != sod) {
if (sod.verify()) {
if (!Arrays.equals(id.getHash(), sod.getCitizenIdentificationHash())) {
throw new ValidatorException("Resumo da identificação do cidadão não coincide com o resumo no SOD");
}
if (null != address && !Arrays.equals(address.getHash(), sod.getCitizenAddressHash())) {
throw new ValidatorException("Resumo da morada do cidadão não coincide com o resumo no SOD");
}
if (null != photo && !Arrays.equals(photo.getHash(), sod.getCitizenPhoto())) {
throw new ValidatorException("Resumo da fotografia do cidadão não coincide com o resumo no SOD");
}
} else {
throw new ValidatorException("Não foi possivel validar o SOD");
}
if (!getCitizenIdentification().getCivilianIdNumber().equals(getCivilianIdNumber(certificate))) {
throw new ValidatorException("Os dados enviados não coincidem com os dados do certificado");
}
}
} catch (InvalidKeyException | SignatureException | InvalidNameException | LeafCertificateValidationException | NoSuchAlgorithmException | UnsupportedEncodingException | SODException ex) {
throw new ValidatorException(ex.getMessage(), ex);
}
}
private String getCivilianIdNumber(X509Certificate certificate) throws InvalidNameException {
String serialNumber = BCStyle.INSTANCE.oidToDisplayName(BCStyle.SERIALNUMBER);
Map<String, String> oidMap = new HashMap<>();
oidMap.put(BCStyle.SERIALNUMBER.getId(), serialNumber);
String subjectName = certificate.getSubjectX500Principal().getName(X500Principal.RFC2253, oidMap);
for (Rdn rdn : new LdapName(subjectName).getRdns()) {
if (serialNumber.equalsIgnoreCase(rdn.getType())) {
return rdn.getValue().toString().toLowerCase().replace(BI, "");
}
}
return "";
}
public CitizenAddressAttributes getCitizenAddress(){
return address;
}
public CitizenIdentificationAttributes getCitizenIdentification(){
return id;
}
public CitizenPhotoAttributes getPhotoAttributes(){
return photo;
}
public void setCertificate(X509Certificate certificate) {
this.certificate = certificate;
}
public void setUUID(UUID recvUuid) {
this.uuid = recvUuid;
}
public void setSignature(byte[] signatureBytes) {
this.signatureBytes = signatureBytes;
}
}