/*
*
* 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.certprofile.commonpki;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.isismtt.x509.AdmissionSyntax;
import org.bouncycastle.asn1.isismtt.x509.Admissions;
import org.bouncycastle.asn1.isismtt.x509.ProfessionInfo;
import org.bouncycastle.asn1.x500.DirectoryString;
import org.bouncycastle.asn1.x509.GeneralName;
import org.xipki.commons.common.util.CollectionUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.pki.ca.api.BadCertTemplateException;
import org.xipki.pki.ca.api.profile.ExtensionValue;
/**
* @author Lijun Liao
* @since 2.0.1
*/
public class AdmissionSyntaxOption {
private final boolean critical;
private final GeneralName admissionAuthority;
private final List<AdmissionsOption> admissionsList;
private final boolean inputFromRequestRequired;
private final ExtensionValue extensionValue;
public AdmissionSyntaxOption(final boolean critical, final GeneralName admissionAuthority,
final List<AdmissionsOption> admissionsList) {
this.critical = critical;
this.admissionAuthority = admissionAuthority;
this.admissionsList = ParamUtil.requireNonEmpty("admissionsList", admissionsList);
boolean bo = false;
for (AdmissionsOption ao : admissionsList) {
for (ProfessionInfoOption pio : ao.getProfessionInfos()) {
if (pio.getRegistrationNumberOption() != null
&& pio.getRegistrationNumberOption().getRegex() != null) {
bo = true;
break;
}
}
if (bo) {
break;
}
}
this.inputFromRequestRequired = bo;
if (this.inputFromRequestRequired) {
extensionValue = null;
return;
}
ASN1EncodableVector vec = new ASN1EncodableVector();
for (AdmissionsOption ao : admissionsList) {
List<ProfessionInfoOption> piList = ao.getProfessionInfos();
ProfessionInfo[] pis = new ProfessionInfo[piList.size()];
for (int i = 0; i < pis.length; i++) {
ProfessionInfoOption pio = piList.get(i);
DirectoryString[] professionItems = null;
int size = pio.getProfessionItems().size();
professionItems = new DirectoryString[size];
for (int j = 0; j < size; j++) {
professionItems[j] = new DirectoryString(pio.getProfessionItems().get(j));
}
ASN1OctetString addProfessionInfo = null;
if (pio.getAddProfessionalInfo() != null) {
addProfessionInfo = new DEROctetString(pio.getAddProfessionalInfo());
}
String registrationNumber = null;
if (pio.getRegistrationNumberOption() != null) {
registrationNumber = pio.getRegistrationNumberOption().getConstant();
}
pis[i] = new ProfessionInfo(pio.getNamingAuthority(), professionItems,
pio.getProfessionOids().toArray(new ASN1ObjectIdentifier[0]),
registrationNumber, addProfessionInfo);
}
vec.add(new Admissions(ao.getAdmissionAuthority(), ao.getNamingAuthority(), pis));
}
extensionValue = new ExtensionValue(critical,
new AdmissionSyntax(admissionAuthority, new DERSequence(vec)));
}
public GeneralName getAdmissionAuthority() {
return admissionAuthority;
}
public List<AdmissionsOption> getAdmissionsList() {
return admissionsList;
}
public boolean isInputFromRequestRequired() {
return inputFromRequestRequired;
}
public ExtensionValue getExtensionValue(final List<List<String>> registrationNumbersList)
throws BadCertTemplateException {
if (!this.inputFromRequestRequired) {
return this.extensionValue;
}
if (CollectionUtil.isEmpty(registrationNumbersList)) {
throw new BadCertTemplateException("registrationNumbersList must not be empty");
}
final int n = registrationNumbersList.size();
if (n != this.admissionsList.size()) {
throw new BadCertTemplateException("invalid size of Admissions in AdmissionSyntax: "
+ "is=" + n + ", expected=" + this.admissionsList.size());
}
// check registrationNumbers
List<List<String>> newRegNumbersList = new ArrayList<>(this.admissionsList.size());
for (int i = 0; i < n; i++) {
AdmissionsOption ao = this.admissionsList.get(i);
List<ProfessionInfoOption> pi = ao.getProfessionInfos();
List<String> registrationNumbers = registrationNumbersList.get(i);
final int k = registrationNumbers.size();
if (k != pi.size()) {
throw new BadCertTemplateException("invalid size of ProfessionInfo in Admissions["
+ i + "], is=" + k + ", expected=" + pi.size());
}
List<String> newRegNumbers = new ArrayList<>(k);
newRegNumbersList.add(newRegNumbers);
for (int j = 0; j < k; j++) {
RegistrationNumberOption option = pi.get(j).getRegistrationNumberOption();
if (option == null || option.getConstant() != null) {
continue;
}
Pattern regex = option.getRegex();
String regNum = registrationNumbers.get(j);
if (regNum == null || !regex.matcher(regNum).matches()) {
throw new BadCertTemplateException("invalid registrationNumber[" + i + "][" + j
+ "]: '" + regNum + "'");
}
newRegNumbers.add(regNum);
}
}
ASN1EncodableVector vec = new ASN1EncodableVector();
for (int i = 0; i < this.admissionsList.size(); i++) {
AdmissionsOption ao = this.admissionsList.get(i);
List<ProfessionInfoOption> piList = ao.getProfessionInfos();
ProfessionInfo[] pis = new ProfessionInfo[piList.size()];
for (int j = 0; j < pis.length; j++) {
ProfessionInfoOption pio = piList.get(j);
DirectoryString[] professionItems = null;
int size = pio.getProfessionItems().size();
professionItems = new DirectoryString[size];
for (int k = 0; k < size; k++) {
professionItems[k] = new DirectoryString(pio.getProfessionItems().get(k));
}
ASN1OctetString addProfessionInfo = null;
if (pio.getAddProfessionalInfo() != null) {
addProfessionInfo = new DEROctetString(pio.getAddProfessionalInfo());
}
RegistrationNumberOption regNumOption = pio.getRegistrationNumberOption();
String registrationNumber = null;
if (regNumOption != null) {
if (regNumOption.getConstant() != null) {
registrationNumber = regNumOption.getConstant();
} else {
registrationNumber = newRegNumbersList.get(i).get(j);
}
}
pis[i] = new ProfessionInfo(pio.getNamingAuthority(), professionItems,
pio.getProfessionOids().toArray(new ASN1ObjectIdentifier[0]),
registrationNumber, addProfessionInfo);
}
vec.add(new Admissions(ao.getAdmissionAuthority(), ao.getNamingAuthority(), pis));
}
return new ExtensionValue(critical,
new AdmissionSyntax(admissionAuthority, new DERSequence(vec)));
}
}