package com.opentrust.spi.crypto;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import javax.crypto.interfaces.DHPublicKey;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
/*
* Created on 6 juin 2005
*/
/**
* Classe utilitaire présentant des méthodes pour la gestion des certificats
*
* @author mehdi.bouallagui
*/
public class CertificateHelper {
public static final String RDN_CN_TYPE = "cn";
public static final String CERTIFICATE_BEGIN = "-----BEGIN CERTIFICATE-----";
public static final String CERTIFICATE_END = "-----END CERTIFICATE-----";
public static final int KEY_USAGE_LEN = 9;
public static final String[] KEY_USAGE = new String[] { "digitalSignature",
"nonRepudiation", "keyEncipherment", "dataEncipherment",
"keyAgreement", "keyCertSign", "cRLSign", "encipherOnly",
"decipherOnly" };
public static final int KEY_USAGE_DIGITALSIGNATURE_INDEX = 0;
public static final int KEY_USAGE_NONREPUDIATION_INDEX = 1;
public static final int KEY_USAGE_KEYENCIPHERMENT_INDEX = 2;
public static final int KEY_USAGE_DATAENCIPHERMENT_INDEX = 3;
public static final int KEY_USAGE_KEYAGREEMENT_INDEX = 4;
public static final int KEY_USAGE_KEYCERTSIGN_INDEX = 5;
public static final int KEY_USAGE_CRLSIGN_INDEX = 6;
public static final int KEY_USAGE_ENCIPHERONLY_INDEX = 7;
public static final int KEY_USAGE_DECIPHERONLY_INDEX = 8;
public final static Hashtable<String, String> EXTENDED_KEY_USAGE_TABLE = new Hashtable<String, String>();
static {
EXTENDED_KEY_USAGE_TABLE.put("anyExtendedKeyUsage", "2.5.29.37.0");
EXTENDED_KEY_USAGE_TABLE.put("serverAuth", "1.3.6.1.5.5.7.3.1");
EXTENDED_KEY_USAGE_TABLE.put("clientAuth", "1.3.6.1.5.5.7.3.2");
EXTENDED_KEY_USAGE_TABLE.put("codeSigning", "1.3.6.1.5.5.7.3.3");
EXTENDED_KEY_USAGE_TABLE.put("emailProtection", "1.3.6.1.5.5.7.3.4");
EXTENDED_KEY_USAGE_TABLE.put("ipsecEndSystem", "1.3.6.1.5.5.7.3.5");
EXTENDED_KEY_USAGE_TABLE.put("ipsecTunnel", "1.3.6.1.5.5.7.3.6");
EXTENDED_KEY_USAGE_TABLE.put("ipsecUser", "1.3.6.1.5.5.7.3.7");
EXTENDED_KEY_USAGE_TABLE.put("timeStamping", "1.3.6.1.5.5.7.3.8");
EXTENDED_KEY_USAGE_TABLE.put("ocspSigning", "1.3.6.1.5.5.7.3.9");
EXTENDED_KEY_USAGE_TABLE.put("iKEIntermediate", "1.3.6.1.5.5.8.2.2");
EXTENDED_KEY_USAGE_TABLE.put("microsoftSGC", "1.3.6.1.4.1.311.10.3.3");
EXTENDED_KEY_USAGE_TABLE.put("netscapeSGC", "2.16.840.1.113730.4.1");
}
/**
* construit un objet Certificate à partir d'une chaîne encodée en base 64.
* qui est une représentation PEM d'un certificat. Il peut contenir ou non les
* délimteur "-----BEGIN/END CERTIFICATE-----".
*
* @param b64String
* chaine en base 64 représentant un certificat (PEM ou DER)
* @return certificat
* @throws IOException
* dans le cas d'un problème de décodage du base 64
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
*/
public static Certificate getCertificate(String b64String)
throws IOException, CertificateException {
Certificate cert = null;
if (!b64String.startsWith(CERTIFICATE_BEGIN)
&& !b64String.endsWith(CERTIFICATE_END)) {
byte[] certBytes = Base64.decode(b64String);
if (certBytes == null || certBytes.length == 0) {
throw new CertificateException("Decoded certificate is null or empty");
}
cert = getCertificate(certBytes);
} else {
InputStream is = new ByteArrayInputStream(
b64String.getBytes());
cert = getCertificate(is);
is.close();
}
return cert;
}
/**
* construit un byte[] correspondant à un objet Certificate à partir d'une
* chaîne encodée en base 64. qui est une représentation PEM d'un
* certificat. Il peut contenir ou non les délimteur
* "-----BEGIN/END CERTIFICATE-----".
*
* @param b64String
* chaine en base 64 représentant un certificat (PEM ou DER)
* @return certificat
* @throws IOException
* dans le cas d'un problème de décodage du base 64
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
*/
public static byte[] getCertificateEncoded(String b64String)
throws IOException, CertificateException {
byte[] certBytes = null;
if (!b64String.startsWith(CERTIFICATE_BEGIN)
&& !b64String.endsWith(CERTIFICATE_END)) {
certBytes = Base64.decode(b64String);
if (certBytes == null || certBytes.length == 0)
throw new CertificateException("Decoded certificate is null or empty");
} else {
InputStream is = new ByteArrayInputStream(
b64String.getBytes());
Certificate cert = getCertificate(is);
is.close();
certBytes = cert.getEncoded();
}
return certBytes;
}
/**
* construit un objet Certificate à partir de sa réprésentation en tableau
* d'octets
*
* @param certificateBytes
* tableau représentant le certificat
* @return certificat
* @throws IOException
* dans le cas d'un problème de manipulation du contenu du
* tableau
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
*/
public static Certificate getCertificate(byte[] certificateBytes)
throws IOException, CertificateException {
InputStream byteArrayInputStream = new ByteArrayInputStream(
certificateBytes);
Certificate certificate = getCertificate(byteArrayInputStream);
byteArrayInputStream.close();
return certificate;
}
/**
* construit un object Certificate à partir d'un nom de fichier
*
* @param fileName
* chemin d'accès au fichier contenant le certificat
* @param informDER
* true si le format d'entrée est DER, sinon false (PEM)
* @return certificat
* @throws IOException
* @throws CertificateException
*/
public static Certificate getCertificate(String fileName, boolean informDER)
throws IOException, CertificateException {
File cert = new File(fileName);
if (informDER) {
byte[] content = FileHelper.load(cert.getAbsolutePath());
return CertificateHelper.getCertificate(content);
} else {
return CertificateHelper.getPEMCertificate(cert);
}
}
/**
* construit un objet Certificate à partir d'un fichier. Le fichier contient
* une représentation PEM d'un certificat. Il peut contenir ou non les
* délimteur "-----BEGIN/END CERTIFICATE-----".
*
* @param file
* fichier contenant le certificat
* @return certificat
* @throws IOException
* dans le cas de problème de lecture du fichier
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
*/
public static Certificate getPEMCertificate(File file) throws IOException,
CertificateException {
byte[] fileBytes = FileHelper.load(file.getAbsolutePath());
String fileString = new String(fileBytes);
if (!fileString.startsWith(CERTIFICATE_BEGIN)
&& !fileString.endsWith(CERTIFICATE_END)) {
return getCertificate(fileString);
}
InputStream fileInputStream = new FileInputStream(file);
Certificate certificate = getCertificate(fileInputStream);
fileInputStream.close();
return certificate;
}
/**
* construit un objet Certificate à partir d'un fichier. Le fichier contient
* une représentation de certificat DER, ou PEM avec les délimiteurs
* "-----BEGIN/END CERTIFICATE-----"
*
* @param file
* fichier contenant le certificat
* @return certificat
* @throws IOException
* dans le cas de problème de lecture du fichier
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
*/
public static Certificate getCertificate(File file) throws IOException, CertificateException {
InputStream fileInputStream = new FileInputStream(file);
Certificate certificate = getCertificate(fileInputStream);
fileInputStream.close();
return certificate;
}
/**
* construit un objet Certificate à partir d'un stream. Utile pour la
* génération des certificats 'on the fly'.
*
* @param inputStream
* stream permettant de lire un certificat
* @return certificat
* @throws CertificateException
* dans le cas d'un problème de parsing du certificat
* @throws IOException
* dans le cas d'un problème de lecture du certificat
*/
private static Certificate getCertificate(InputStream inputStream)
throws CertificateException, IOException {
Certificate certificate = null;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
//TODO Why do we keep on reading the input stream after parsing a certificate ?
while (inputStream.available() > 0) {
Certificate cert = cf.generateCertificate(inputStream);
if (cert != null) {
certificate = cert;
}
}
if (certificate == null) {
throw new CertificateException("Could not parse certificate");
}
return certificate;
}
/**
* calcule l'empreinte numérique d'un certificat
*
* @param certificate
* certificat dont on veut calculer l'empreinte
* @param algorithm
* algorithme de hachage utilisé pour calculer l'empreinte
* @return tableau d'octet représentant empreinte numérique du certificat
* @throws CertificateException
* en cad de problème d'encodage du certificat
* @throws NoSuchAlgorithmException
* dans le cas où aucun provider n'est enregistré pour gérer
* l'algorithme en entrée
*/
public static byte[] getFingerPrint(Certificate certificate,
String algorithm) throws CertificateException,
NoSuchAlgorithmException {
return DigestHelper.getDigest(certificate.getEncoded(), algorithm);
}
/**
* finds key usage string from a java key usage array representation
*
* @param keyUsageIndex
* key usage java index
* @return key usage string
*/
public static String getKeyUsage(int keyUsageIndex) {
try {
return KEY_USAGE[keyUsageIndex];
} catch (Exception e) {
throw new IllegalArgumentException(
"Cannot find key usage string representation", e);
}
}
/**
*
* @param certificate
* @param keyUsageIndex
* @return Returns true if certificate has the key usage corresponding
* to given key usage index, false otherwise.
*/
public static boolean hasKeyUsage(X509Certificate certificate, int keyUsageIndex) {
if (certificate == null) {
throw new IllegalArgumentException("Argument certificate is null");
}
if (keyUsageIndex < 0) {
throw new IllegalArgumentException("Argument keyUsageIndex is negative");
}
boolean[] usages = certificate.getKeyUsage();
if (usages != null && usages.length > keyUsageIndex
&& usages[keyUsageIndex])
return true;
else
return false;
}
public static String getExtendedKeyUsageOID(String keyUsage) {
return (String) EXTENDED_KEY_USAGE_TABLE.get(keyUsage);
}
public static String getExtendedKeyUsageName(String oid) {
Enumeration k = EXTENDED_KEY_USAGE_TABLE.keys();
while (k.hasMoreElements()) {
String key = (String) k.nextElement();
if (key.equals(oid)) {
return EXTENDED_KEY_USAGE_TABLE.get(key);
}
}
return null;
}
/**
* finds key usage java structure index from key usage string
*
* @param keyUsageString
* key usage string
* @return key usage index
*/
public static int getKeyUsageIndex(String keyUsageString) {
for (int i = 0; i < KEY_USAGE.length; i++) {
if (keyUsageString.equalsIgnoreCase(KEY_USAGE[i]))
return i;
}
throw new IllegalArgumentException(keyUsageString
+ " does not represent a valid key usage");
}
/**
* gets certificate key length
*
* @param certificate
* certificate for which the key length must be returned
* @return certificate key length
* @throws KeyException
*/
public static int getKeyLength(Certificate certificate) throws KeyException {
if (certificate == null) {
throw new IllegalArgumentException("certificate cannot be null");
}
return getKeyLength(certificate.getPublicKey());
}
/**
* gets public key length
*
* @param publicKey
* key for which the key length must be returned
* @return public key length
* @throws KeyException
*/
public static int getKeyLength(PublicKey publicKey) throws KeyException {
if (publicKey == null) {
throw new IllegalArgumentException("key cannot be null");
}
int keyLength = -1;
if (publicKey instanceof java.security.interfaces.RSAPublicKey) {
keyLength = ((RSAPublicKey) publicKey).getModulus().bitLength();
}
if (publicKey instanceof java.security.interfaces.DSAPublicKey) {
keyLength = ((DSAPublicKey) publicKey).getParams().getP()
.bitLength();
}
if (publicKey instanceof javax.crypto.interfaces.DHPublicKey) {
keyLength = ((DHPublicKey) publicKey).getParams().getP()
.bitLength();
}
if (keyLength != -1) {
return keyLength;
}
throw new KeyException("Cannot get key length");
}
public static String getRdnValueFromDN(String dn, String rdnType) {
String result = null;
if (dn == null) {
throw new IllegalArgumentException("DN cannot be null");
}
if (rdnType == null) {
throw new IllegalArgumentException("RDN type cannot be null");
}
try {
LdapName ldapdn = new LdapName(dn);
for (Rdn rdn : ldapdn.getRdns()) {
if (rdn.getType().equalsIgnoreCase(rdnType)) {
result = (String) rdn.getValue();
break;
}
}
} catch (InvalidNameException e) {
throw new IllegalArgumentException("Cannot parse DN value " + dn
+ ", " + e.getMessage());
}
return result;
}
public static String getCNFromDN(String dn) {
return getRdnValueFromDN(dn, RDN_CN_TYPE);
}
public static String getLabel(X509Certificate certificate) {
if (certificate == null) {
return null;
}
String dn = certificate.getSubjectDN().getName();
String cn = CertificateHelper.getCNFromDN(dn);
if (cn != null && cn.length() > 0) {
return cn;
}
return dn;
}
public static List<Certificate> getCertificateFullPath(
Certificate certificate, List<Certificate> certificateList, Date referenceDate)
{
CertStore certStore = null;
try {
certStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certificateList));
} catch (Exception e) {
ExceptionHandler.handle(e);
}
return getCertificateFullPath(certificate, certStore, referenceDate);
}
public static List<Certificate> getCertificateFullPath(
Certificate targetCertificate, CertStore certStore, Date referenceDate)
{
Collection<? extends Certificate> certificates = null;
try {
certificates = certStore.getCertificates(null);
} catch (CertStoreException e) {
ExceptionHandler.handle(e);
}
if (certificates == null || certificates.isEmpty()) {
ExceptionHandler.handle(new CertPathBuilderException(
"no certificates found to build certificate chain"));
}
HashSet<TrustAnchor> rootCertificates = new HashSet<TrustAnchor>();
for (Certificate certificate : certificates) {
String issuerDN = ((X509Certificate) certificate).getIssuerDN()
.getName();
String subjectDN = ((X509Certificate) certificate).getSubjectDN()
.getName();
if (issuerDN.equals(subjectDN)) {
rootCertificates.add(new TrustAnchor(
(X509Certificate) certificate, null));
}
}
X509CertSelector targetConstraints = new X509CertSelector();
targetConstraints.setCertificate((X509Certificate) targetCertificate);
PKIXBuilderParameters params = null;
try {
params = new PKIXBuilderParameters(rootCertificates,
targetConstraints);
} catch (Exception e) {
ExceptionHandler.handle(e);
}
params.setRevocationEnabled(false);
params.addCertStore(certStore);
params.setDate(referenceDate);
CertPathBuilder builder = null;
try {
builder = CertPathBuilder.getInstance("PKIX");
} catch (NoSuchAlgorithmException e) {
ExceptionHandler.handle(e);
}
// catch (NoSuchProviderException e) {
// ExceptionHandler.handle(e);
// }
PKIXCertPathBuilderResult certPathBuilderResult = null;
try {
certPathBuilderResult = (PKIXCertPathBuilderResult) builder
.build(params);
} catch (Exception e) {
String msg = null;
if (e.getCause()!=null) {
if (e.getCause() instanceof CertificateExpiredException) {
msg = "Certification path could not be validated because certificate is expired ["+e.getCause().getMessage()+"]";
} else if (e.getCause().getCause()!=null && e.getCause().getCause() instanceof CertificateExpiredException) {
msg = "Certification path could not be validated because certificate is expired ["+e.getCause().getCause().getMessage()+"]";
} else if (e.getCause() instanceof CertificateNotYetValidException) {
msg = "Certification path could not be validated because certificate is not yet valid ["+e.getCause().getMessage()+"]";
} else if (e.getCause().getCause()!=null && e.getCause().getCause() instanceof CertificateNotYetValidException) {
msg = "Certification path could not be validated because certificate is not yet valid ["+e.getCause().getCause().getMessage()+"]";
}
}
ExceptionHandler.handle(e,msg);
}
// TODO optimizations ?
List<? extends Certificate> certFullPath = certPathBuilderResult
.getCertPath().getCertificates();
ArrayList<Certificate> certFullPathArrayList = new ArrayList<Certificate>(
certFullPath.size() + 1);
certFullPathArrayList.addAll(certFullPath);
certFullPathArrayList.add(certPathBuilderResult.getTrustAnchor()
.getTrustedCert());
return certFullPathArrayList;
}
public static Certificate getIssuerCertificate(
Certificate certificate,
CertStore... certificatesStores) {
String issuerDN = ((X509Certificate) certificate).getIssuerDN()
.getName();
String issuerCN=getCNFromDN(issuerDN);
Collection<Certificate> certificates = new ArrayList<Certificate>();
if (certificatesStores!=null) {
for (CertStore certificatesStore : certificatesStores) {
if (certificatesStore!=null) {
try {
Collection<? extends Certificate> certToAdd = certificatesStore.getCertificates(null);
if (certToAdd!=null) {
certificates.addAll(certificatesStore.getCertificates(null));
}
} catch (CertStoreException e) {
ExceptionHandler.handle(e);
}
}
}
}
if (certificates == null || certificates.isEmpty()) {
ExceptionHandler
.handle(new CertPathBuilderException(
"cannot find issuer certificate from empty certificate store"));
}
for (Certificate issuercertificate : certificates) {
String subjectDN = ((X509Certificate) issuercertificate).getSubjectDN()
.getName();
String subjectCN = getCNFromDN(subjectDN);
if (issuerCN.equals(subjectCN)) {
try {
certificate.verify(issuercertificate.getPublicKey());
return issuercertificate;
} catch (Exception e) {
// skip to the next candidate
}
}
}
ExceptionHandler.handle(new CertPathBuilderException(
"cannot find issuer certificate of "
+ ((X509Certificate) certificate).getSubjectDN()
.getName()
+ " issued by "
+ ((X509Certificate) certificate).getIssuerDN()
.getName()));
return null;
}
/**
* This methode convert a X509 Distinguished Name String into a normalized
* form, as is to say, the resulting string :
* <ul>
* <li>have the RelativeDN elements in an ascendant order "emailaddress,
* sn, cn, ou, o, c", the input dn must contains at least two different RDN
* elements from this list of wellknown elements</li>
* <li>RDN separator is ", "</li>
* <li>case, quoted char and escape char in each RDN are preserved in the
* output string</li>
* <li>RDN value special characters are recognized ( escape character,
* etc.)</li>
* <li>Input DN must use "," as RDN separator (with or without space)</li>
* <li>the rdn name is converted to emailaddress</li>
* </ul>
* <p>
* Tested successfully with the following sample :
* <ul>
* <li>cn=\"Joë, Martîn\", ou=Sales, ou=Company\\, The, o=workl, c=fr</li>
* <li>c=fr,o=workl,ou=Company\\, The,ou=Sales\\0Dbo,cn=Joe Martin</li>
* <li>EMAILADDRESS=fdsfsd@fr.fr, CN=Joe marting, BL=CT, C=fr</li>
* <li>SN=toto,EMAILADDRESS=fdsfsd@fr.fr, CN=Joe marting,BL=CT, C=fr</li>
* <li>foofoo=fr,o=workl,ou=Company\\, The,ou=Sales,cn=Joe Martin</li>
* </ul>
*/
public static String normalizeDN(String dn) {
// First, split the dn into RDN elements, the X509NameTokenizer
// recognize escape character
X509NameTokenizerKeepChar rdnToken = new X509NameTokenizerKeepChar(dn);
// Now guess the RDN elements order (from root to leaf or the contrary)
// The guessing method consists in looking for wellknown RDN keys
// and compares their absolute position in the string to determine if
// the order is ascendant (emailaddress, sn, cn, ou, o, c)
// or descendant (c, o, ou, cn, sn, emailaddress)
String[] rdnName = { "emailaddress=", "sn=", "cn=", "ou=", "o=", "c=" };
int orderIsUndefined = -1;
int orderIsUnknown = 0;
int orderIsAscendant = 1;
int orderIsDescendant = 2;
int presumedOrder = orderIsUnknown;
String elt;
int prevPosition = -1;
ArrayList rdnArrayList = new ArrayList();
while (rdnToken.hasMoreTokens()) {
elt = rdnToken.nextToken();
if (elt.startsWith("e=")) {
elt = elt.replaceFirst("e=", "emailaddress=");
} else if (elt.startsWith("E=")) {
elt = elt.replaceFirst("E=", "EMAILADDRESS=");
}
rdnArrayList.add(elt);
}
int i = 0;
while (i < rdnName.length && presumedOrder != orderIsUndefined) {
for (int j = 0; j < rdnArrayList.size(); ++j) {
elt = (String) rdnArrayList.get(j);
if (elt.toLowerCase().startsWith(rdnName[i])) {
if (prevPosition == -1) {
prevPosition = j;
} else if (prevPosition <= j) {
if (presumedOrder == orderIsUnknown) {
presumedOrder = orderIsAscendant;
} else if (presumedOrder == orderIsDescendant) {
presumedOrder = orderIsUndefined;
}
} else {
if (presumedOrder == orderIsUnknown) {
presumedOrder = orderIsDescendant;
} else if (presumedOrder == orderIsAscendant) {
presumedOrder = orderIsUndefined;
}
}
}
}
++i;
}
StringBuffer outputDN = new StringBuffer(dn.length());
if (presumedOrder != orderIsDescendant) {
boolean isFirst = true;
for (int k = 0; k < rdnArrayList.size(); ++k) {
if (isFirst) {
isFirst = false;
} else {
outputDN.append(", ");
}
outputDN.append((String) rdnArrayList.get(k));
}
} else {
boolean isFirst = true;
for (int k = rdnArrayList.size() - 1; k >= 0; --k) {
if (isFirst) {
isFirst = false;
} else {
outputDN.append(", ");
}
outputDN.append((String) rdnArrayList.get(k));
}
}
return outputDN.toString();
}
/**
* class for breaking up an X500 Name into it's component tokens, ala
* java.util.StringTokenizer. We need this class as some of the lightweight
* Java environment don't support classes like StringTokenizer.
* <p>
* This modified version keeps escape and quote chars in output and suppress
* white space between rdn name and "="
*/
public static class X509NameTokenizerKeepChar {
private String value;
private int index;
private char seperator;
private StringBuffer buf = new StringBuffer();
public X509NameTokenizerKeepChar(String oid) {
this(oid, ',');
}
public X509NameTokenizerKeepChar(String oid, char seperator) {
this.value = oid;
this.index = -1;
this.seperator = seperator;
}
public boolean hasMoreTokens() {
return (index != value.length());
}
public String nextToken() {
if (index == value.length()) {
return null;
}
int end = index + 1;
boolean quoted = false;
boolean escaped = false;
boolean namepart = true;
boolean valuepart = false;
buf.setLength(0);
while (end != value.length()) {
char c = value.charAt(end);
if (c == '"') {
if (!escaped) {
quoted = !quoted;
}
buf.append(c);
escaped = false;
} else {
if (escaped || quoted) {
buf.append(c);
escaped = false;
} else if (c == '\\') {
buf.append(c);
escaped = true;
} else if (c == seperator) {
break;
} else if (c == '=' && namepart) {
buf.append(c);
namepart = false;
valuepart = true;
end++;
continue;
} else if (c == ' ' || c == '\t') {
if (!namepart && !valuepart) {
buf.append(c);
}
end++;
continue;
} else {
buf.append(c);
}
}
if (valuepart) {
valuepart = false;
}
end++;
}
index = end;
return buf.toString().trim();
}
}
public static boolean isAuthorityCertificate(Certificate certificate) {
return (((X509Certificate) certificate).getBasicConstraints()!=-1);
}
public static byte[] getSubjectKeyIdentifier(X509Certificate certificate) throws CertificateException {
byte[] result = null;
try {
byte[] extvalue = certificate.getExtensionValue(X509Extensions.SubjectKeyIdentifier.getId());
if (extvalue != null) {
SubjectKeyIdentifier keyId = new SubjectKeyIdentifierStructure(extvalue);
result = keyId.getKeyIdentifier();
}
} catch (IOException e) {
throw new CertificateException("Error retrieving certificate subject key identifier for subject "
+certificate.getSubjectX500Principal().getName(), e);
}
return result;
}
public static byte[] getAuthorityKeyIdentifier(X509Certificate certificate) throws CertificateException {
byte[] result = null;
try {
byte[] extvalue = certificate.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
if (extvalue != null) {
AuthorityKeyIdentifier keyId = new AuthorityKeyIdentifierStructure(extvalue);
result = keyId.getKeyIdentifier();
}
} catch (IOException e) {
throw new CertificateException("Error retrieving certificate authority key identifier for subject "
+certificate.getSubjectX500Principal().getName(), e);
}
return result;
}
}