/*************************************************************************
* *
* EJBCA: The OpenSource Certificate Authority *
* *
* 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.ejbca.util.cert;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DERGeneralizedTime;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERString;
import org.bouncycastle.asn1.x509.Attribute;
import org.bouncycastle.asn1.x509.X509DefaultEntryConverter;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.ejbca.util.CertTools;
/**
* A class for reading values from SubjectDirectoryAttributes extension.
*
* @author Tomas Gustavsson
* @version $Id: SubjectDirAttrExtension.java 11142 2011-01-11 14:19:42Z aveen4711 $
*/
public class SubjectDirAttrExtension extends CertTools {
private static final Logger log = Logger.getLogger(SubjectDirAttrExtension.class);
/**
* inhibits creation of new SubjectDirAttrExtension
*/
private SubjectDirAttrExtension() {
}
/**
* SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
*
* Attribute ::= SEQUENCE {
* type AttributeType,
* values SET OF AttributeValue }
* -- at least one value is required
*
* AttributeType ::= OBJECT IDENTIFIER
* AttributeValue ::= ANY
*
* SubjectDirectoryAttributes is of form
* dateOfBirth=<19590927>, placeOfBirth=<string>, gender=<M/F>, countryOfCitizenship=<two letter ISO3166>, countryOfResidence=<two letter ISO3166>
*
* Supported subjectDirectoryAttributes are the ones above
*
* @param certificate containing subject directory attributes
* @return String containing directoryAttributes of form the form specified above or null if no directoryAttributes exist.
* Values in returned String is from CertTools constants.
* DirectoryAttributes not supported are simply not shown in the resulting string.
* @throws java.lang.Exception
*/
public static String getSubjectDirectoryAttributes(Certificate certificate) throws Exception {
log.debug("Search for SubjectAltName");
String result = "";
if (certificate instanceof X509Certificate) {
X509Certificate x509cert = (X509Certificate) certificate;
DERObject obj = CertTools.getExtensionValue(x509cert, X509Extensions.SubjectDirectoryAttributes.getId());
if (obj == null) {
return null;
}
ASN1Sequence seq = (ASN1Sequence)obj;
String prefix = "";
FastDateFormat dateF = FastDateFormat.getInstance("yyyyMMdd");
for (int i = 0; i < seq.size(); i++) {
Attribute attr = Attribute.getInstance(seq.getObjectAt(i));
if (!StringUtils.isEmpty(result)) {
prefix = ", ";
}
if (attr.getAttrType().getId().equals(id_pda_dateOfBirth)) {
ASN1Set set = attr.getAttrValues();
// Come on, we'll only allow one dateOfBirth, we're not allowing such frauds with multiple birth dates
DERGeneralizedTime time = DERGeneralizedTime.getInstance(set.getObjectAt(0));
Date date = time.getDate();
String dateStr = dateF.format(date);
result += prefix + "dateOfBirth="+dateStr;
}
if (attr.getAttrType().getId().equals(id_pda_placeOfBirth)) {
ASN1Set set = attr.getAttrValues();
// same here only one placeOfBirth
String pb = ((DERString)set.getObjectAt(0)).getString();
result += prefix + "placeOfBirth="+pb;
}
if (attr.getAttrType().getId().equals(id_pda_gender)) {
ASN1Set set = attr.getAttrValues();
// same here only one gender
String g = ((DERString)set.getObjectAt(0)).getString();
result += prefix + "gender="+g;
}
if (attr.getAttrType().getId().equals(id_pda_countryOfCitizenship)) {
ASN1Set set = attr.getAttrValues();
// same here only one citizenship
String g = ((DERString)set.getObjectAt(0)).getString();
result += prefix + "countryOfCitizenship="+g;
}
if (attr.getAttrType().getId().equals(id_pda_countryOfResidence)) {
ASN1Set set = attr.getAttrValues();
// same here only one residence
String g = ((DERString)set.getObjectAt(0)).getString();
result += prefix + "countryOfResidence="+g;
}
}
}
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
/**
* From subjectDirAttributes string as defined in getSubjectDirAttribute
* @param dirAttr string of SubjectDirectoryAttributes
* @return A Collection of ASN.1 Attribute (org.bouncycastle.asn1.x509), or an empty Collection, never null
* @see #getSubjectDirectoryAttributes(Certificate)
*/
public static Collection<Attribute> getSubjectDirectoryAttributes(String dirAttr) {
ArrayList<Attribute> ret = new ArrayList<Attribute>();
Attribute attr = null;
String value = CertTools.getPartFromDN(dirAttr, "countryOfResidence");
if (!StringUtils.isEmpty(value)) {
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(new DERPrintableString(value));
attr = new Attribute(new DERObjectIdentifier(id_pda_countryOfResidence),new DERSet(vec));
ret.add(attr);
}
value = CertTools.getPartFromDN(dirAttr, "countryOfCitizenship");
if (!StringUtils.isEmpty(value)) {
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(new DERPrintableString(value));
attr = new Attribute(new DERObjectIdentifier(id_pda_countryOfCitizenship),new DERSet(vec));
ret.add(attr);
}
value = CertTools.getPartFromDN(dirAttr, "gender");
if (!StringUtils.isEmpty(value)) {
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(new DERPrintableString(value));
attr = new Attribute(new DERObjectIdentifier(id_pda_gender),new DERSet(vec));
ret.add(attr);
}
value = CertTools.getPartFromDN(dirAttr, "placeOfBirth");
if (!StringUtils.isEmpty(value)) {
ASN1EncodableVector vec = new ASN1EncodableVector();
X509DefaultEntryConverter conv = new X509DefaultEntryConverter();
DERObject obj = conv.getConvertedValue(new DERObjectIdentifier(id_pda_placeOfBirth), value);
vec.add(obj);
attr = new Attribute(new DERObjectIdentifier(id_pda_placeOfBirth),new DERSet(vec));
ret.add(attr);
}
// dateOfBirth that is a GeneralizedTime
// The correct format for this is YYYYMMDD, it will be padded to YYYYMMDD120000Z
value = CertTools.getPartFromDN(dirAttr, "dateOfBirth");
if (!StringUtils.isEmpty(value)) {
if (value.length() == 8) {
value += "120000Z"; // standard format according to rfc3739
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(new DERGeneralizedTime(value));
attr = new Attribute(new DERObjectIdentifier(id_pda_dateOfBirth),new DERSet(vec));
ret.add(attr);
} else {
log.error("Wrong length of data for 'dateOfBirth', should be of format YYYYMMDD, skipping...");
}
}
return ret;
}
}