Java tutorial
package org.votingsystem.model.currency; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.pkcs.CertificationRequestInfo; import org.bouncycastle.jce.PKCS10CertificationRequest; import org.votingsystem.dto.currency.CurrencyCSRDto; import org.votingsystem.dto.currency.CurrencyCertExtensionDto; import org.votingsystem.dto.currency.TransactionVSDto; import org.votingsystem.model.CertificateVS; import org.votingsystem.model.MessageSMIME; import org.votingsystem.model.TagVS; import org.votingsystem.model.UserVS; import org.votingsystem.signature.smime.SMIMEMessage; import org.votingsystem.signature.util.CMSUtils; import org.votingsystem.signature.util.CertUtils; import org.votingsystem.signature.util.CertificationRequestVS; import org.votingsystem.throwable.ExceptionVS; import org.votingsystem.throwable.ValidationExceptionVS; import org.votingsystem.util.*; import org.votingsystem.util.currency.Payment; import javax.persistence.*; import java.io.*; import java.math.BigDecimal; import java.security.cert.CertificateEncodingException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.time.LocalDate; import java.time.Period; import java.time.ZoneId; import java.util.*; import java.util.logging.Logger; import static javax.persistence.GenerationType.IDENTITY; /** * License: https://github.com/votingsystem/votingsystem/wiki/Licencia */ @Entity @Table(name = "Currency") public class Currency extends EntityVS implements Serializable { private static Logger log = Logger.getLogger(Currency.class.getSimpleName()); public static final long serialVersionUID = 1L; public enum State { OK, EXPENDED, LAPSED, ERROR; } //Lapsed -> for not expended time limited currency @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) private Long id; @Column(name = "subject") private String subject; @Column(name = "amount") private BigDecimal amount = null; @Column(name = "paymentMethod") @Enumerated(EnumType.STRING) private Payment paymentMethod; @Column(name = "currency", nullable = false) private String currencyCode; @Column(name = "isTimeLimited") private Boolean isTimeLimited = Boolean.FALSE; @Column(name = "hashCertVS") private String hashCertVS; @Column(name = "originHashCertVS") private String originHashCertVS; @Column(name = "currencyServerURL") private String currencyServerURL; @Column(name = "reason") private String reason; @Column(name = "metaInf") private String metaInf; @Column(name = "batchUUID") private String batchUUID; @Column(name = "serialNumber", unique = true, nullable = false) private Long serialNumber; @Column(name = "content", nullable = false) private byte[] content; @Column(name = "state", nullable = false) @Enumerated(EnumType.STRING) private State state; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "toUserVS") private UserVS toUserVS; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "authorityCertificateVS") private CertificateVS authorityCertificateVS; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "tagVS", nullable = false) private TagVS tag; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "transactionvs") private TransactionVS transactionVS; @OneToOne private MessageSMIME cancelMessage; @OneToOne private MessageSMIME messageSMIME; @Temporal(TemporalType.TIMESTAMP) @Column(name = "validFrom", length = 23) private Date validFrom; @Temporal(TemporalType.TIMESTAMP) @Column(name = "validTo", length = 23) private Date validTo; @Temporal(TemporalType.TIMESTAMP) @Column(name = "dateCreated", length = 23) private Date dateCreated; @Temporal(TemporalType.TIMESTAMP) @Column(name = "lastUpdated", length = 23) private Date lastUpdated; @Transient private TypeVS operation; @Transient private BigDecimal batchAmount; @Transient private CertificationRequestVS certificationRequest; @Transient private PKCS10CertificationRequest csr; @Transient private X509Certificate x509AnonymousCert; @Transient private transient SMIMEMessage smimeMessage; @Transient private File file; @Transient private String toUserIBAN; @Transient private String toUserName; @Transient private Currency.CertSubject certSubject; @Transient private CurrencyCertExtensionDto certExtensionDto; public Currency() { } public Currency(SMIMEMessage smimeMessage) throws Exception { this.smimeMessage = smimeMessage; x509AnonymousCert = smimeMessage.getCurrencyCert(); CurrencyCertExtensionDto certExtensionDto = CertUtils.getCertExtensionData(CurrencyCertExtensionDto.class, x509AnonymousCert, ContextVS.CURRENCY_OID); initCertData(certExtensionDto, smimeMessage.getCurrencyCert().getSubjectDN().toString()); validateSignedData(); } public Currency(PKCS10CertificationRequest csr) throws ExceptionVS, IOException { this.csr = csr; CertificationRequestInfo info = csr.getCertificationRequestInfo(); Enumeration csrAttributes = info.getAttributes().getObjects(); CurrencyCertExtensionDto certExtensionDto = null; while (csrAttributes.hasMoreElements()) { DERTaggedObject attribute = (DERTaggedObject) csrAttributes.nextElement(); switch (attribute.getTagNo()) { case ContextVS.CURRENCY_TAG: String certAttributeJSONStr = ((DERUTF8String) attribute.getObject()).getString(); certExtensionDto = JSON.getMapper().readValue(certAttributeJSONStr, CurrencyCertExtensionDto.class); break; } } initCertData(certExtensionDto, info.getSubject().toString()); } public Currency initCertData(CurrencyCertExtensionDto certExtensionDto, String subjectDN) throws ExceptionVS { if (certExtensionDto == null) throw new ValidationExceptionVS("error missing cert extension data"); this.certExtensionDto = certExtensionDto; certSubject = new CertSubject(subjectDN, hashCertVS); hashCertVS = certExtensionDto.getHashCertVS(); currencyServerURL = certExtensionDto.getCurrencyServerURL(); if (!certSubject.getCurrencyServerURL().equals(certExtensionDto.getCurrencyServerURL())) throw new ValidationExceptionVS( "currencyServerURL: " + currencyServerURL + " - certSubject: " + certSubject); amount = certExtensionDto.getAmount(); if (certSubject.getCurrencyValue().compareTo(amount) != 0) throw new ValidationExceptionVS("amount: " + amount + " - certSubject: " + certSubject); currencyCode = certExtensionDto.getCurrencyCode(); if (!certSubject.getCurrencyCode().equals(currencyCode)) throw new ValidationExceptionVS("currencyCode: " + currencyCode + " - certSubject: " + certSubject); if (!certSubject.getTag().equals(certExtensionDto.getTag())) throw new ValidationExceptionVS("currencyCode: " + currencyCode + " - certSubject: " + certSubject); return this; } public CertSubject getCertSubject() { return certSubject; } public Currency checkRequestWithDB(Currency currencyRequest) throws ExceptionVS { if (!currencyRequest.getCurrencyServerURL().equals(currencyServerURL)) throw new ExceptionVS("checkRequestWithDB_currencyServerURL"); if (!currencyRequest.getHashCertVS().equals(hashCertVS)) throw new ExceptionVS("checkRequestWithDB_hashCertVS"); if (!currencyRequest.getCurrencyCode().equals(currencyCode)) throw new ExceptionVS("checkRequestWithDB_currencyCode"); if (!currencyRequest.getCertExtensionDto().getTag().equals(tag.getName())) throw new ExceptionVS("checkRequestWithDB_TagVS"); if (currencyRequest.getAmount().compareTo(amount) != 0) throw new ExceptionVS("checkRequestWithDB_amount"); this.smimeMessage = currencyRequest.getSMIME(); this.x509AnonymousCert = currencyRequest.getX509AnonymousCert(); this.toUserVS = currencyRequest.getToUserVS(); this.toUserIBAN = currencyRequest.getToUserIBAN(); this.subject = currencyRequest.getSubject(); return this; } private String getErrorPrefix() { return "ERROR - Currency with hash: " + hashCertVS + " - "; } public void validateSignedData() throws Exception { ObjectNode dataJSON = (ObjectNode) new ObjectMapper().readTree(smimeMessage.getSignedContent()); if (dataJSON.has("amount")) { BigDecimal toUserVSAmount = new BigDecimal(dataJSON.get("amount").asText()); if (amount.compareTo(toUserVSAmount) != 0) throw new ExceptionVS(getErrorPrefix() + "and value '" + amount + "' has signed amount '" + toUserVSAmount + "'"); } else if (dataJSON.has("batchAmount")) { this.batchAmount = new BigDecimal(dataJSON.get("batchAmount").asText()); } if (dataJSON.has("batchUUID")) this.batchUUID = dataJSON.get("batchUUID").asText(); if (dataJSON.has("paymentMethod")) this.paymentMethod = Payment.valueOf(dataJSON.get("paymentMethod").asText()); operation = TypeVS.valueOf(dataJSON.get("operation").asText()); if (TypeVS.CURRENCY_SEND != operation) throw new ExceptionVS("Error - Currency with invalid operation '" + operation.toString() + "'"); if (!currencyCode.equals(dataJSON.get("currencyCode").asText())) throw new ExceptionVS(getErrorPrefix() + "currencyCode '" + currencyCode + "' has signed currencyCode '" + dataJSON.get("currencyCode").asText()); tag = new TagVS(dataJSON.get("tag").asText()); if (!TagVS.WILDTAG.equals(certExtensionDto.getTag()) && !certExtensionDto.getTag().equals(tag.getName())) throw new ExceptionVS(getErrorPrefix() + "tag '" + certExtensionDto.getTag() + "' has signed tag '" + dataJSON.get("tag")); Date signatureTime = smimeMessage.getTimeStampToken().getTimeStampInfo().getGenTime(); if (signatureTime.after(x509AnonymousCert.getNotAfter())) throw new ExceptionVS(getErrorPrefix() + "valid to '" + x509AnonymousCert.getNotAfter().toString() + "' has signature date '" + signatureTime.toString() + "'"); subject = dataJSON.get("subject").asText(); toUserIBAN = dataJSON.get("toUserIBAN").asText(); toUserName = dataJSON.get("toUserName").asText(); if (dataJSON.has("isTimeLimited")) isTimeLimited = dataJSON.get("isTimeLimited").asBoolean(); } public void initSigner(byte[] csrBytes) throws Exception { certificationRequest.initSigner(csrBytes); x509AnonymousCert = certificationRequest.getCertificate(); validFrom = x509AnonymousCert.getNotBefore(); validTo = x509AnonymousCert.getNotAfter(); CurrencyCertExtensionDto certExtensionData = CertUtils.getCertExtensionData(CurrencyCertExtensionDto.class, x509AnonymousCert, ContextVS.CURRENCY_OID); initCertData(certExtensionData, x509AnonymousCert.getSubjectDN().toString()); certSubject.addDateInfo(x509AnonymousCert); } public static Currency load(CertificationRequestVS certificationRequest) throws Exception { Currency currency = new Currency(); currency.setCertificationRequest(certificationRequest); currency.initSigner(certificationRequest.getSignedCsr()); return currency; } public CurrencyCertExtensionDto getCertExtensionDto() { return certExtensionDto; } public void setCertExtensionDto(CurrencyCertExtensionDto certExtensionDto) { this.certExtensionDto = certExtensionDto; } public BigDecimal getBatchAmount() { return batchAmount; } public TypeVS getOperation() { return operation; } public Boolean getIsTimeLimited() { return isTimeLimited; } public void setIsTimeLimited(Boolean isTimeLimited) { this.isTimeLimited = isTimeLimited; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } public String getBatchUUID() { return batchUUID; } public String getToUserIBAN() { return toUserIBAN; } public String getToUserName() { return toUserName; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public Map<String, String> getCertExtensionData() throws IOException { return CertUtils.getCertExtensionData(x509AnonymousCert, ContextVS.CURRENCY_OID); } public SMIMEMessage getSMIME() { return smimeMessage; } public void setSMIME(SMIMEMessage smimeMessage) { this.smimeMessage = smimeMessage; } public X509Certificate getX509AnonymousCert() { return x509AnonymousCert; } public PKCS10CertificationRequest getCsr() { return csr; } public void setCsr(PKCS10CertificationRequest csr) { this.csr = csr; } public Currency(String currencyServerURL, BigDecimal amount, String currencyCode, TagVS tag) { this.amount = amount; this.currencyServerURL = currencyServerURL; this.currencyCode = currencyCode; this.tag = tag; try { setOriginHashCertVS(UUID.randomUUID().toString()); setHashCertVS(CMSUtils.getHashBase64(getOriginHashCertVS(), ContextVS.VOTING_DATA_DIGEST)); certificationRequest = CertificationRequestVS.getCurrencyRequest(ContextVS.KEY_SIZE, ContextVS.SIG_NAME, ContextVS.VOTE_SIGN_MECHANISM, ContextVS.PROVIDER, currencyServerURL, hashCertVS, amount, this.currencyCode, tag.getName()); } catch (Exception ex) { ex.printStackTrace(); } } public Currency loadCertData(X509Certificate x509AnonymousCert, TimePeriod timePeriod, CertificateVS authorityCertificateVS) throws CertificateEncodingException { this.x509AnonymousCert = x509AnonymousCert; this.serialNumber = x509AnonymousCert.getSerialNumber().longValue(); this.content = x509AnonymousCert.getEncoded(); this.state = State.OK; this.validFrom = timePeriod.getDateFrom(); this.validTo = timePeriod.getDateTo(); this.authorityCertificateVS = authorityCertificateVS; return this; } public String getMetaInf() { return metaInf; } public void setMetaInf(String metaInf) { this.metaInf = metaInf; } public byte[] getIssuedCertPEM() throws IOException { return CertUtils.getPEMEncoded(x509AnonymousCert); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getHashCertVS() { return hashCertVS; } public void setHashCertVS(String hashCertVS) { this.hashCertVS = hashCertVS; } public BigDecimal getAmount() { return amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } public String getCurrencyServerURL() { return currencyServerURL; } public void setCurrencyServerURL(String currencyServerURL) { this.currencyServerURL = currencyServerURL; } public String getOriginHashCertVS() { return originHashCertVS; } public void setOriginHashCertVS(String originHashCertVS) { this.originHashCertVS = originHashCertVS; } public MessageSMIME getCancelMessage() { return cancelMessage; } public void setCancelMessage(MessageSMIME cancelMessage) { this.cancelMessage = cancelMessage; } public MessageSMIME getMessageSMIME() { return messageSMIME; } public void setMessageSMIME(MessageSMIME messageSMIME) { this.messageSMIME = messageSMIME; } public Date getValidFrom() { return validFrom; } public void setValidFrom(Date validFrom) { this.validFrom = validFrom; } public Date getValidTo() { return validTo; } public void setValidTo(Date validTo) { this.validTo = validTo; } public Long getSerialNumber() { return serialNumber; } public void setSerialNumber(Long serialNumber) { this.serialNumber = serialNumber; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public Date getDateCreated() { return dateCreated; } public void setDateCreated(Date dateCreated) { this.dateCreated = dateCreated; } public Date getLastUpdated() { return lastUpdated; } public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } public CertificateVS getAuthorityCertificateVS() { return authorityCertificateVS; } public void setAuthorityCertificateVS(CertificateVS authorityCertificateVS) { this.authorityCertificateVS = authorityCertificateVS; } public State getState() { return state; } public Currency setState(State state) { this.state = state; return this; } public String getReason() { return reason; } public Currency setReason(String reason) { this.reason = reason; return this; } public String getCurrencyCode() { return currencyCode; } public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; } public TransactionVS getTransactionVS() { return transactionVS; } public Currency setTransactionVS(TransactionVS transactionParent) { this.transactionVS = transactionParent; return this; } public UserVS getToUserVS() { return toUserVS; } public void setToUserVS(UserVS userVS) { this.toUserVS = userVS; } public CertificationRequestVS getCertificationRequest() { return certificationRequest; } public void setCertificationRequest(CertificationRequestVS certificationRequest) { this.certificationRequest = certificationRequest; } public Payment getPaymentMethod() { return paymentMethod; } public void setPaymentMethod(Payment paymentMethod) { this.paymentMethod = paymentMethod; } public static boolean checkIfTimeLimited(Date notBefore, Date notAfter) { LocalDate notBeforeLD = notBefore.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate notAfterLD = notAfter.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); Period validPeriod = Period.between(notBeforeLD, notAfterLD); return validPeriod.getDays() <= 7;//one week } public TagVS getTag() { if (tag != null) return tag; else return new TagVS(certExtensionDto.getTag()); } public void setTag(TagVS tag) { this.tag = tag; } public void validateReceipt(SMIMEMessage smimeReceipt, Set<TrustAnchor> trustAnchor) throws Exception { if (!smimeMessage.getSigner().getContentDigestBase64() .equals(smimeReceipt.getSigner().getContentDigestBase64())) { throw new ExceptionVS("Signer content digest mismatch"); } for (X509Certificate cert : smimeReceipt.getSignersCerts()) { CertUtils.verifyCertificate(trustAnchor, false, Arrays.asList(cert)); log.info("validateReceipt - Cert validated: " + cert.getSubjectDN().toString()); } this.smimeMessage = smimeReceipt; if (file != null) { FileUtils.copyStreamToFile(new ByteArrayInputStream(ObjectUtils.serializeObject(this)), file); file.renameTo(new File(file.getParent() + File.separator + "EXPENDED_" + file.getName())); } } public TransactionVSDto getSendRequest(String toUserName, String toUserIBAN, String subject, Boolean isTimeLimited) throws ExceptionVS { this.toUserName = toUserName; this.toUserIBAN = toUserIBAN; this.subject = subject; if (isTimeLimited == false && checkIfTimeLimited(x509AnonymousCert.getNotBefore(), x509AnonymousCert.getNotAfter())) { throw new ExceptionVS("Time limited Currency with 'isTimeLimited' signature param set to false"); } return TransactionVSDto.CURRENCY_SEND(toUserName, subject, amount, currencyCode, toUserIBAN, isTimeLimited, tag.getName()); } public CurrencyCSRDto getCSRDto() throws Exception { return new CurrencyCSRDto(new String(certificationRequest.getCsrPEM()), amount, currencyCode, tag.getName()); } public static Map<String, BigDecimal> getCurrencyMap(Collection<Currency> currencyList) { Map<String, BigDecimal> currencyMap = new HashMap<String, BigDecimal>(); for (Currency currency : currencyList) { if (currencyMap.containsKey(currency.getCurrencyCode())) currencyMap.put(currency.getCurrencyCode(), currencyMap.get(currency.getCurrencyCode()).add(currency.getAmount())); else currencyMap.put(currency.getCurrencyCode(), currency.getAmount()); } return currencyMap; } public static class TransactionVSData { private String subject, toUserName, toUserIBAN; private Boolean isTimeLimited; public String getSubject() { return subject; } public String getToUserName() { return toUserName; } public String getToUserIBAN() { return toUserIBAN; } public Boolean getIsTimeLimited() { return isTimeLimited; } public TransactionVSData() { } public TransactionVSData(String subject, String toUserName, String toUserIBAN, Boolean isTimeLimited) { this.subject = subject; this.toUserName = toUserName; this.toUserIBAN = toUserIBAN; this.isTimeLimited = isTimeLimited; } public TransactionVSData(Map formData) { this.subject = (String) formData.get("subject"); this.toUserName = (String) formData.get("toUserName"); this.toUserIBAN = (String) formData.get("toUserIBAN"); this.isTimeLimited = (Boolean) formData.get("isTimeLimited"); } public Map getJSON() { Map result = new HashMap<>(); result.put("subject", subject); result.put("toUserName", toUserName); result.put("toUserIBAN", toUserIBAN); result.put("isTimeLimited", isTimeLimited); return result; } } public static class CertSubject implements Serializable { public static final long serialVersionUID = 1L; private String currencyCode; private BigDecimal currencyValue; private String currencyServerURL; private String tag; private String hashCertVS; private Map<String, String> dataMap; private Date notBefore; private Date notAfter; public CertSubject(String subjectDN, String hashCertVS) { this.hashCertVS = hashCertVS; if (subjectDN.contains("CURRENCY_CODE:")) currencyCode = subjectDN.split("CURRENCY_CODE:")[1].split(",")[0]; if (subjectDN.contains("CURRENCY_VALUE:")) currencyValue = new BigDecimal(subjectDN.split("CURRENCY_VALUE:")[1].split(",")[0]); if (subjectDN.contains("TAG:")) tag = subjectDN.split("TAG:")[1].split(",")[0]; if (subjectDN.contains("currencyServerURL:")) currencyServerURL = subjectDN.split("currencyServerURL:")[1].split(",")[0]; dataMap = new HashMap<>(); dataMap.put("currencyServerURL", currencyServerURL); dataMap.put("currencyCode", currencyCode); dataMap.put("currencyValue", currencyValue.toString()); dataMap.put("tag", tag); dataMap.put("hashCertVS", hashCertVS); } public void addDateInfo(X509Certificate x509AnonymousCert) { this.notBefore = x509AnonymousCert.getNotBefore(); this.notAfter = x509AnonymousCert.getNotAfter(); dataMap.put("notBefore", DateUtils.getDateStr(x509AnonymousCert.getNotBefore())); dataMap.put("notAfter", DateUtils.getDateStr(x509AnonymousCert.getNotAfter())); } public String getCurrencyCode() { return this.currencyCode; } public BigDecimal getCurrencyValue() { return this.currencyValue; } public String getCurrencyServerURL() { return this.currencyServerURL; } public String getTag() { return this.tag; } public Map<String, String> getDataMap() { return dataMap; } public Date getNotBefore() { return notBefore; } public Date getNotAfter() { return notAfter; } } private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); try { if (smimeMessage != null) s.writeObject(smimeMessage.getBytes()); else s.writeObject(null); } catch (Exception ex) { ex.printStackTrace(); } } private void readObject(ObjectInputStream s) throws Exception { s.defaultReadObject(); byte[] smimeMessageBytes = (byte[]) s.readObject(); if (smimeMessageBytes != null) { smimeMessage = new SMIMEMessage(new ByteArrayInputStream(smimeMessageBytes)); } if (x509AnonymousCert != null) { validFrom = x509AnonymousCert.getNotBefore(); validTo = x509AnonymousCert.getNotAfter(); } } }