/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software 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 any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.certificates.certificate;
import java.io.Serializable;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.log4j.Logger;
import org.cesecore.dbprotection.ProtectedData;
import org.cesecore.dbprotection.ProtectionStringBuilder;
import org.cesecore.util.Base64;
import org.cesecore.util.CertTools;
/**
* Base64 encoded certificates.
* If the property "database.useSeparateCertificateTable" is true then it will
* be one new row in this table for each certificate added to {@link CertificateData}.
* If the property is false then this table will not be used.
*
* @version $Id: Base64CertData.java 28776 2018-04-26 12:54:17Z samuellb $
*/
@Entity
@Table(name = "Base64CertData")
public class Base64CertData extends ProtectedData implements Serializable {
private static final long serialVersionUID = 4132839902195978822L;
private static final Logger log = Logger.getLogger(Base64CertData.class);
private String fingerprint = "";
private String base64Cert;
private int rowVersion = 0;
private String rowProtection;
/**
* Storing an encoded certificate. Called only when
* {@link CertificateData#CertificateData(Certificate, java.security.PublicKey, String, String, int, int, int, int, String, long, boolean, boolean)}
* is called with useBase64CertTable set to true.
* @param incert the (X509)Certificate to be stored in the database.
*/
public Base64CertData(Certificate incert) {
// Extract all fields to store with the certificate.
try {
setBase64Cert(new String(Base64.encode(incert.getEncoded())));
setFingerprint( CertTools.getFingerprintAsString(incert) );
} catch (CertificateEncodingException cee) {
final String msg = "Can't extract DER encoded certificate information.";
log.error(msg, cee);
throw new RuntimeException(msg);
}
}
/**
* Copy constructor
*/
public Base64CertData(final Base64CertData copy) {
setBase64Cert(copy.getBase64Cert());
setFingerprint(copy.getFingerprint());
setRowProtection(copy.getRowProtection());
setRowVersion(copy.getRowVersion());
}
public Base64CertData() {
}
/**
* Fingerprint of certificate
*
* @return fingerprint
*/
// @Id @Column
public String getFingerprint() {
return this.fingerprint;
}
/**
* Fingerprint of certificate
*
* @param fingerprint fingerprint
*/
public void setFingerprint(String fingerprint) {
this.fingerprint = fingerprint;
}
/**
* The encoded certificate.
* Called from {@link CertificateData#getCertificate(EntityManager)} when
* there is no encoded certificate in {@link CertificateData}.
*
* @return base64 encoded certificate
*/
// @Column @Lob
public String getBase64Cert() {
return this.base64Cert;
}
/**
* The certificate itself
*
* @param base64Cert base64 encoded certificate
*/
public void setBase64Cert(String base64Cert) {
this.base64Cert = base64Cert;
}
// @Version @Column
public int getRowVersion() {
return this.rowVersion;
}
public void setRowVersion(int rowVersion) {
this.rowVersion = rowVersion;
}
// @Column @Lob
@Override
public String getRowProtection() {
return this.rowProtection;
}
@Override
public void setRowProtection(String rowProtection) {
this.rowProtection = rowProtection;
}
//
// Comparators
//
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Base64CertData)) {
return false;
}
return equals((Base64CertData) obj);
}
public boolean equals(final Base64CertData other) {
if (other==null) {
return false;
}
if (!fingerprint.equals(other.fingerprint)) {
return false;
}
if (!base64Cert.equals(other.base64Cert)) {
return false;
}
if (rowProtection!=null && !rowProtection.equals(other.rowProtection)) {
return false;
}
if (rowProtection==null && other.rowProtection!=null) {
return false;
}
if (rowVersion!=other.rowVersion) {
return false;
}
return true;
}
@Override
public int hashCode() {
return fingerprint.hashCode() * 11;
}
//
// Search functions.
//
/** @return the found entity instance or null if the entity does not exist */
public static Base64CertData findByFingerprint(EntityManager entityManager, String fingerprint) {
return entityManager.find(Base64CertData.class, fingerprint);
}
/** @return the number of entries with the given parameter */
public static long getCount(EntityManager entityManager) {
final Query countQuery = entityManager.createQuery("SELECT COUNT(a) FROM Base64CertData a");
return ((Long) countQuery.getSingleResult()).longValue(); // Always returns a result
}
//
// Start Database integrity protection methods
//
@Transient
@Override
protected String getProtectString(final int version) {
final ProtectionStringBuilder build = new ProtectionStringBuilder(3000);
// What is important to protect here is the data that we define, id, name and certificate profile data
// rowVersion is automatically updated by JPA, so it's not important, it is only used for optimistic locking
build.append(getFingerprint()).append(getBase64Cert());
if (log.isDebugEnabled()) {
// Some profiling
if (build.length() > 3000) {
log.debug("Base64CertData.getProtectString gives size: " + build.length());
}
}
return build.toString();
}
@Transient
@Override
protected int getProtectVersion() {
return 1;
}
@PrePersist
@PreUpdate
@Override
protected void protectData() {
super.protectData();
}
@PostLoad
@Override
protected void verifyData() {
super.verifyData();
}
@Override
@Transient
protected String getRowId() {
return getFingerprint();
}
//
// End Database integrity protection methods
//
}