/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.pki.ca.server.impl.cmp; import java.math.BigInteger; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bouncycastle.util.encoders.Hex; import org.xipki.commons.common.util.CollectionUtil; import org.xipki.commons.common.util.ParamUtil; import org.xipki.pki.ca.api.publisher.x509.X509CertificateInfo; /** * @author Lijun Liao * @since 2.0.0 */ class PendingCertificatePool { private static class MyEntry { private final BigInteger certReqId; private final long waitForConfirmTill; private final X509CertificateInfo certInfo; private final byte[] certHash; MyEntry(final BigInteger certReqId, final long waitForConfirmTill, final X509CertificateInfo certInfo) { this.certReqId = ParamUtil.requireNonNull("certReqId", certReqId); this.certInfo = ParamUtil.requireNonNull("certInfo", certInfo); this.waitForConfirmTill = waitForConfirmTill; this.certHash = certInfo.getHashAlgo().hash(certInfo.getCert().getEncodedCert()); } @Override public int hashCode() { return certReqId.hashCode() + 961 * (int) waitForConfirmTill + 31 * certInfo.hashCode(); } @Override public boolean equals(final Object obj) { if (!(obj instanceof MyEntry)) { return false; } MyEntry another = (MyEntry) obj; return certReqId.equals(another.certReqId) && certInfo.equals(another.certInfo); } } // class MyEntry private final Map<String, Set<MyEntry>> map = new HashMap<>(); PendingCertificatePool() { } void addCertificate(final byte[] transactionId, final BigInteger certReqId, final X509CertificateInfo certInfo, final long waitForConfirmTill) { ParamUtil.requireNonNull("transactionId", transactionId); ParamUtil.requireNonNull("certInfo", certInfo); if (certInfo.isAlreadyIssued()) { return; } String hexTid = Hex.toHexString(transactionId); MyEntry myEntry = new MyEntry(certReqId, waitForConfirmTill, certInfo); synchronized (map) { Set<MyEntry> entries = map.get(hexTid); if (entries == null) { entries = new HashSet<>(); map.put(hexTid, entries); } entries.add(myEntry); } } X509CertificateInfo removeCertificate(final byte[] transactionId, final BigInteger certReqId, final byte[] certHash) { ParamUtil.requireNonNull("transactionId", transactionId); ParamUtil.requireNonNull("certReqId", certReqId); ParamUtil.requireNonNull("certHash", certHash); String hexTid = Hex.toHexString(transactionId); MyEntry retEntry = null; synchronized (map) { Set<MyEntry> entries = map.get(hexTid); if (entries == null) { return null; } for (MyEntry entry : entries) { if (certReqId.equals(entry.certReqId)) { retEntry = entry; break; } } if (retEntry != null) { if (Arrays.equals(certHash, retEntry.certHash)) { entries.remove(retEntry); if (CollectionUtil.isEmpty(entries)) { map.remove(hexTid); } } } } return (retEntry == null) ? null : retEntry.certInfo; } Set<X509CertificateInfo> removeCertificates(final byte[] transactionId) { ParamUtil.requireNonNull("transactionId", transactionId); String hexId = Hex.toHexString(transactionId); Set<MyEntry> entries; synchronized (map) { entries = map.remove(hexId); } if (entries == null) { return null; } Set<X509CertificateInfo> ret = new HashSet<>(); for (MyEntry myEntry :entries) { ret.add(myEntry.certInfo); } return ret; } Set<X509CertificateInfo> removeConfirmTimeoutedCertificates() { synchronized (map) { if (CollectionUtil.isEmpty(map)) { return null; } long now = System.currentTimeMillis(); Set<X509CertificateInfo> ret = new HashSet<>(); for (String tid : map.keySet()) { Set<MyEntry> entries = map.get(tid); for (MyEntry entry : entries) { if (entry.waitForConfirmTill < now) { ret.add(entry.certInfo); } } } return ret; } } }