/*************************************************************************
* *
* 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.io.IOException;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.qualified.ETSIQCObjectIdentifiers;
import org.bouncycastle.asn1.x509.qualified.MonetaryValue;
import org.bouncycastle.asn1.x509.qualified.QCStatement;
import org.bouncycastle.asn1.x509.qualified.RFC3739QCObjectIdentifiers;
import org.bouncycastle.asn1.x509.qualified.SemanticsInformation;
import org.ejbca.util.CertTools;
/**
* A class for reading values from QC-statement extension.
*
* @author Tomas Gustavsson
* @version $Id: QCStatementExtension.java 11623 2011-03-28 15:59:01Z jeklund $
*/
public final class QCStatementExtension extends CertTools {
private static final Logger log = Logger.getLogger(QCStatementExtension.class);
/**
* inhibits creation of new SubjectDirAttrExtension
*/
private QCStatementExtension() {
super();
}
/** Returns true if the certificate contains a QC-statements extension.
*
* @param cert Certificate containing the extension
* @return true or false.
* @throws IOException if there is a problem parsing the certificate
*/
public static boolean hasQcStatement(final Certificate cert) throws IOException {
boolean ret = false;
if (cert instanceof X509Certificate) {
final X509Certificate x509cert = (X509Certificate) cert;
final DERObject obj = getExtensionValue(x509cert, X509Extensions.QCStatements.getId());
if (obj != null) {
ret = true;
}
}
return ret;
}
/** Returns all the 'statementId' defined in the QCStatement extension (rfc3739).
*
* @param cert Certificate containing the extension
* @return Collection of String with the oid, for example "1.1.1.2", or empty Collection if no identifier is found, never returns null.
* @throws IOException if there is a problem parsing the certificate
*/
public static Collection<String> getQcStatementIds(final Certificate cert) throws IOException {
final ArrayList<String> ret = new ArrayList<String>();
if (cert instanceof X509Certificate) {
final X509Certificate x509cert = (X509Certificate) cert;
final DERObject obj = getExtensionValue(x509cert, X509Extensions.QCStatements.getId());
if (obj == null) {
return ret;
}
final ASN1Sequence seq = (ASN1Sequence)obj;
for (int i = 0; i < seq.size(); i++) {
final QCStatement qc = QCStatement.getInstance(seq.getObjectAt(i));
final DERObjectIdentifier oid = qc.getStatementId();
if (oid != null) {
ret.add(oid.getId());
}
}
}
return ret;
}
/** Returns the value limit ETSI QCStatement if present.
*
* @param cert Certificate possibly containing the QCStatement extension
* @return String with the value and currency (ex '50000 SEK')or null if the extension is not present
* @throws IOException if there is a problem parsing the certificate
*/
public static String getQcStatementValueLimit(final Certificate cert) throws IOException {
String ret = null;
if (cert instanceof X509Certificate) {
final X509Certificate x509cert = (X509Certificate) cert;
final DERObject obj = getExtensionValue(x509cert, X509Extensions.QCStatements.getId());
if (obj == null) {
return null;
}
final ASN1Sequence seq = (ASN1Sequence)obj;
MonetaryValue mv = null;
// Look through all the QCStatements and see if we have a stadard ETSI LimitValue
for (int i = 0; i < seq.size(); i++) {
final QCStatement qc = QCStatement.getInstance(seq.getObjectAt(i));
final DERObjectIdentifier oid = qc.getStatementId();
if ((oid != null) && oid.equals(ETSIQCObjectIdentifiers.id_etsi_qcs_LimiteValue)) {
// We MAY have a MonetaryValue object here
final ASN1Encodable enc = qc.getStatementInfo();
if (enc != null) {
mv = MonetaryValue.getInstance(enc);
// We can break the loop now, we got it!
break;
}
}
}
if (mv != null) {
final BigInteger amount = mv.getAmount();
final BigInteger exp = mv.getExponent();
final BigInteger ten = BigInteger.valueOf(10);
// A possibly gotcha here if the monetary value is larger than what fits in a long...
final long value = amount.longValue() * (ten.pow(exp.intValue())).longValue();
if (value < 0) {
log.error("ETSI LimitValue amount is < 0.");
}
final String curr = mv.getCurrency().getAlphabetic();
if (curr == null) {
log.error("ETSI LimitValue currency is null");
}
if ( (value >= 0) && (curr != null) ) {
ret = value + " "+curr;
}
}
}
return ret;
}
/** Returns the 'NameRegistrationAuthorities' defined in the QCStatement extension (rfc3739).
*
* @param cert Certificate containing the extension
* @return String with for example 'rfc822Name=foo2bar.se, rfc822Name=bar2foo.se' etc. Supports email, dns and uri name, or null of no RAs are found.
* @throws IOException if there is a problem parsing the certificate
*/
public static String getQcStatementAuthorities(final Certificate cert) throws IOException {
String ret = null;
if (cert instanceof X509Certificate) {
final X509Certificate x509cert = (X509Certificate) cert;
final DERObject obj = getExtensionValue(x509cert, X509Extensions.QCStatements.getId());
if (obj == null) {
return null;
}
final ASN1Sequence seq = (ASN1Sequence)obj;
SemanticsInformation si = null;
// Look through all the QCStatements and see if we have a standard RFC3739 pkixQCSyntax
for (int i = 0; i < seq.size(); i++) {
final QCStatement qc = QCStatement.getInstance(seq.getObjectAt(i));
final DERObjectIdentifier oid = qc.getStatementId();
if ((oid != null) && (oid.equals(RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v1) || oid.equals(RFC3739QCObjectIdentifiers.id_qcs_pkixQCSyntax_v2))) {
// We MAY have a SemanticsInformation object here
final ASN1Encodable enc = qc.getStatementInfo();
if (enc != null) {
si = SemanticsInformation.getInstance(enc);
// We can break the loop now, we got it!
break;
}
}
}
if (si != null) {
final GeneralName[] gns = si.getNameRegistrationAuthorities();
if (gns == null) {
return null;
}
final StringBuilder strBuf = new StringBuilder();
for (int i = 0; i < gns.length; i++) {
final GeneralName gn = gns[i];
if (strBuf.length() != 0) {
// Append comma so we get nice formatting if there are more than one authority
strBuf.append(", ");
}
final String str = getGeneralNameString(gn.getTagNo(), gn.getName());
if (str != null) {
strBuf.append(str);
}
}
if (strBuf.length() > 0) {
ret = strBuf.toString();
}
}
}
return ret;
}
}