0001: /*
0002: * Copyright 2004 by Paulo Soares.
0003: *
0004: * The contents of this file are subject to the Mozilla Public License Version 1.1
0005: * (the "License"); you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0007: *
0008: * Software distributed under the License is distributed on an "AS IS" basis,
0009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0010: * for the specific language governing rights and limitations under the License.
0011: *
0012: * The Original Code is 'iText, a free JAVA-PDF library'.
0013: *
0014: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0015: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0016: * All Rights Reserved.
0017: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0018: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0019: *
0020: * Contributor(s): all the names of the contributors are added in the source code
0021: * where applicable.
0022: *
0023: * Alternatively, the contents of this file may be used under the terms of the
0024: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0025: * provisions of LGPL are applicable instead of those above. If you wish to
0026: * allow use of your version of this file only under the terms of the LGPL
0027: * License and not to allow others to use your version of this file under
0028: * the MPL, indicate your decision by deleting the provisions above and
0029: * replace them with the notice and other provisions required by the LGPL.
0030: * If you do not delete the provisions above, a recipient may use your version
0031: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0032: *
0033: * This library is free software; you can redistribute it and/or modify it
0034: * under the terms of the MPL as stated above or under the terms of the GNU
0035: * Library General Public License as published by the Free Software Foundation;
0036: * either version 2 of the License, or any later version.
0037: *
0038: * This library is distributed in the hope that it will be useful, but WITHOUT
0039: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0040: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0041: * details.
0042: *
0043: * If you didn't download this code from the following link, you should check if
0044: * you aren't using an obsolete version:
0045: * http://www.lowagie.com/iText/
0046: */
0047: package com.lowagie.text.pdf;
0048:
0049: import java.io.ByteArrayInputStream;
0050: import java.io.ByteArrayOutputStream;
0051: import java.io.File;
0052: import java.io.FileInputStream;
0053: import java.io.IOException;
0054: import java.security.InvalidKeyException;
0055: import java.security.KeyStore;
0056: import java.security.MessageDigest;
0057: import java.security.NoSuchAlgorithmException;
0058: import java.security.NoSuchProviderException;
0059: import java.security.PrivateKey;
0060: import java.security.Signature;
0061: import java.security.SignatureException;
0062: import java.security.cert.CRL;
0063: import java.security.cert.CRLException;
0064: import java.security.cert.Certificate;
0065: import java.security.cert.CertificateException;
0066: import java.security.cert.X509CRL;
0067: import java.security.cert.X509Certificate;
0068: import java.util.ArrayList;
0069: import java.util.Arrays;
0070: import java.util.Calendar;
0071: import java.util.Collection;
0072: import java.util.Enumeration;
0073: import java.util.GregorianCalendar;
0074: import java.util.HashMap;
0075: import java.util.HashSet;
0076: import java.util.Iterator;
0077: import java.util.Set;
0078:
0079: import com.lowagie.text.ExceptionConverter;
0080: import java.math.BigInteger;
0081: import org.bouncycastle.asn1.ASN1EncodableVector;
0082: import org.bouncycastle.asn1.ASN1InputStream;
0083: import org.bouncycastle.asn1.ASN1OutputStream;
0084: import org.bouncycastle.asn1.ASN1Sequence;
0085: import org.bouncycastle.asn1.ASN1Set;
0086: import org.bouncycastle.asn1.ASN1TaggedObject;
0087: import org.bouncycastle.asn1.DERConstructedSet;
0088: import org.bouncycastle.asn1.DERInteger;
0089: import org.bouncycastle.asn1.DERNull;
0090: import org.bouncycastle.asn1.DERObject;
0091: import org.bouncycastle.asn1.DERObjectIdentifier;
0092: import org.bouncycastle.asn1.DEROctetString;
0093: import org.bouncycastle.asn1.DERSequence;
0094: import org.bouncycastle.asn1.DERSet;
0095: import org.bouncycastle.asn1.DERString;
0096: import org.bouncycastle.asn1.DERTaggedObject;
0097: import org.bouncycastle.asn1.DERUTCTime;
0098: import org.bouncycastle.jce.provider.X509CRLParser;
0099: import org.bouncycastle.jce.provider.X509CertParser;
0100: import org.bouncycastle.util.StreamParsingException;
0101:
0102: /**
0103: * This class does all the processing related to signing and verifying a PKCS#7
0104: * signature.
0105: * <p>
0106: * It's based in code found at org.bouncycastle.
0107: */
0108: public class PdfPKCS7 {
0109:
0110: private byte sigAttr[];
0111: private byte digestAttr[];
0112: private int version, signerversion;
0113: private Set digestalgos;
0114: private Collection certs, crls;
0115: private X509Certificate signCert;
0116: private byte[] digest;
0117: private MessageDigest messageDigest;
0118: private String digestAlgorithm, digestEncryptionAlgorithm;
0119: private Signature sig;
0120: private transient PrivateKey privKey;
0121: private byte RSAdata[];
0122: private boolean verified;
0123: private boolean verifyResult;
0124: private byte externalDigest[];
0125: private byte externalRSAdata[];
0126:
0127: private static final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
0128: private static final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
0129: private static final String ID_MD5 = "1.2.840.113549.2.5";
0130: private static final String ID_MD2 = "1.2.840.113549.2.2";
0131: private static final String ID_SHA1 = "1.3.14.3.2.26";
0132: private static final String ID_RSA = "1.2.840.113549.1.1.1";
0133: private static final String ID_DSA = "1.2.840.10040.4.1";
0134: private static final String ID_CONTENT_TYPE = "1.2.840.113549.1.9.3";
0135: private static final String ID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
0136: private static final String ID_SIGNING_TIME = "1.2.840.113549.1.9.5";
0137: private static final String ID_MD2RSA = "1.2.840.113549.1.1.2";
0138: private static final String ID_MD5RSA = "1.2.840.113549.1.1.4";
0139: private static final String ID_SHA1RSA = "1.2.840.113549.1.1.5";
0140: private static final String ID_ADBE_REVOCATION = "1.2.840.113583.1.1.8";
0141: /**
0142: * Holds value of property reason.
0143: */
0144: private String reason;
0145:
0146: /**
0147: * Holds value of property location.
0148: */
0149: private String location;
0150:
0151: /**
0152: * Holds value of property signDate.
0153: */
0154: private Calendar signDate;
0155:
0156: /**
0157: * Holds value of property signName.
0158: */
0159: private String signName;
0160:
0161: /**
0162: * Verifies a signature using the sub-filter adbe.x509.rsa_sha1.
0163: * @param contentsKey the /Contents key
0164: * @param certsKey the /Cert key
0165: * @param provider the provider or <code>null</code> for the default provider
0166: * @throws SecurityException on error
0167: * @throws InvalidKeyException on error
0168: * @throws CertificateException on error
0169: * @throws NoSuchProviderException on error
0170: * @throws NoSuchAlgorithmException on error
0171: * @throws IOException on error
0172: */
0173: public PdfPKCS7(byte[] contentsKey, byte[] certsKey, String provider)
0174: throws SecurityException, InvalidKeyException,
0175: CertificateException, NoSuchProviderException,
0176: NoSuchAlgorithmException, IOException,
0177: StreamParsingException {
0178: X509CertParser cr = new X509CertParser();
0179: cr.engineInit(new ByteArrayInputStream(certsKey));
0180: certs = cr.engineReadAll();
0181: signCert = (X509Certificate) certs.iterator().next();
0182: crls = new ArrayList();
0183: ASN1InputStream in = new ASN1InputStream(
0184: new ByteArrayInputStream(contentsKey));
0185: digest = ((DEROctetString) in.readObject()).getOctets();
0186: if (provider == null)
0187: sig = Signature.getInstance("SHA1withRSA");
0188: else
0189: sig = Signature.getInstance("SHA1withRSA", provider);
0190: sig.initVerify(signCert.getPublicKey());
0191: }
0192:
0193: /**
0194: * Verifies a signature using the sub-filter adbe.pkcs7.detached or
0195: * adbe.pkcs7.sha1.
0196: * @param contentsKey the /Contents key
0197: * @param provider the provider or <code>null</code> for the default provider
0198: * @throws SecurityException on error
0199: * @throws CRLException on error
0200: * @throws InvalidKeyException on error
0201: * @throws CertificateException on error
0202: * @throws NoSuchProviderException on error
0203: * @throws NoSuchAlgorithmException on error
0204: */
0205: public PdfPKCS7(byte[] contentsKey, String provider)
0206: throws SecurityException, CRLException,
0207: InvalidKeyException, CertificateException,
0208: NoSuchProviderException, NoSuchAlgorithmException,
0209: StreamParsingException {
0210: ASN1InputStream din = new ASN1InputStream(
0211: new ByteArrayInputStream(contentsKey));
0212:
0213: //
0214: // Basic checks to make sure it's a PKCS#7 SignedData Object
0215: //
0216: DERObject pkcs;
0217:
0218: try {
0219: pkcs = din.readObject();
0220: } catch (IOException e) {
0221: throw new SecurityException(
0222: "can't decode PKCS7SignedData object");
0223: }
0224: if (!(pkcs instanceof ASN1Sequence)) {
0225: throw new SecurityException(
0226: "Not a valid PKCS#7 object - not a sequence");
0227: }
0228: ASN1Sequence signedData = (ASN1Sequence) pkcs;
0229: DERObjectIdentifier objId = (DERObjectIdentifier) signedData
0230: .getObjectAt(0);
0231: if (!objId.getId().equals(ID_PKCS7_SIGNED_DATA))
0232: throw new SecurityException(
0233: "Not a valid PKCS#7 object - not signed data");
0234: ASN1Sequence content = (ASN1Sequence) ((DERTaggedObject) signedData
0235: .getObjectAt(1)).getObject();
0236: // the positions that we care are:
0237: // 0 - version
0238: // 1 - digestAlgorithms
0239: // 2 - possible ID_PKCS7_DATA
0240: // (the certificates and crls are taken out by other means)
0241: // last - signerInfos
0242:
0243: // the version
0244: version = ((DERInteger) content.getObjectAt(0)).getValue()
0245: .intValue();
0246:
0247: // the digestAlgorithms
0248: digestalgos = new HashSet();
0249: Enumeration e = ((ASN1Set) content.getObjectAt(1)).getObjects();
0250: while (e.hasMoreElements()) {
0251: ASN1Sequence s = (ASN1Sequence) e.nextElement();
0252: DERObjectIdentifier o = (DERObjectIdentifier) s
0253: .getObjectAt(0);
0254: digestalgos.add(o.getId());
0255: }
0256:
0257: // the certificates and crls
0258: X509CertParser cr = new X509CertParser();
0259: cr.engineInit(new ByteArrayInputStream(contentsKey));
0260: certs = cr.engineReadAll();
0261: X509CRLParser cl = new X509CRLParser();
0262: cl.engineInit(new ByteArrayInputStream(contentsKey));
0263: crls = cl.engineReadAll();
0264:
0265: // the possible ID_PKCS7_DATA
0266: ASN1Sequence rsaData = (ASN1Sequence) content.getObjectAt(2);
0267: if (rsaData.size() > 1) {
0268: DEROctetString rsaDataContent = (DEROctetString) ((DERTaggedObject) rsaData
0269: .getObjectAt(1)).getObject();
0270: RSAdata = rsaDataContent.getOctets();
0271: }
0272:
0273: // the signerInfos
0274: int next = 3;
0275: while (content.getObjectAt(next) instanceof DERTaggedObject)
0276: ++next;
0277: ASN1Set signerInfos = (ASN1Set) content.getObjectAt(next);
0278: if (signerInfos.size() != 1)
0279: throw new SecurityException(
0280: "This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
0281: ASN1Sequence signerInfo = (ASN1Sequence) signerInfos
0282: .getObjectAt(0);
0283: // the positions that we care are
0284: // 0 - version
0285: // 1 - the signing certificate serial number
0286: // 2 - the digest algorithm
0287: // 3 or 4 - digestEncryptionAlgorithm
0288: // 4 or 5 - encryptedDigest
0289: signerversion = ((DERInteger) signerInfo.getObjectAt(0))
0290: .getValue().intValue();
0291: // Get the signing certificate
0292: ASN1Sequence issuerAndSerialNumber = (ASN1Sequence) signerInfo
0293: .getObjectAt(1);
0294: BigInteger serialNumber = ((DERInteger) issuerAndSerialNumber
0295: .getObjectAt(1)).getValue();
0296: for (Iterator i = certs.iterator(); i.hasNext();) {
0297: X509Certificate cert = (X509Certificate) i.next();
0298: if (serialNumber.equals(cert.getSerialNumber())) {
0299: signCert = cert;
0300: break;
0301: }
0302: }
0303: if (signCert == null) {
0304: throw new SecurityException(
0305: "Can't find signing certificate with serial "
0306: + serialNumber.toString(16));
0307: }
0308: digestAlgorithm = ((DERObjectIdentifier) ((ASN1Sequence) signerInfo
0309: .getObjectAt(2)).getObjectAt(0)).getId();
0310: next = 3;
0311: if (signerInfo.getObjectAt(next) instanceof ASN1TaggedObject) {
0312: ASN1TaggedObject tagsig = (ASN1TaggedObject) signerInfo
0313: .getObjectAt(next);
0314: ASN1Sequence sseq = (ASN1Sequence) tagsig.getObject();
0315: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
0316: ASN1OutputStream dout = new ASN1OutputStream(bOut);
0317: try {
0318: ASN1EncodableVector attribute = new ASN1EncodableVector();
0319: for (int k = 0; k < sseq.size(); ++k) {
0320: attribute.add(sseq.getObjectAt(k));
0321: }
0322: dout.writeObject(new DERSet(attribute));
0323: dout.close();
0324: } catch (IOException ioe) {
0325: }
0326: sigAttr = bOut.toByteArray();
0327:
0328: for (int k = 0; k < sseq.size(); ++k) {
0329: ASN1Sequence seq2 = (ASN1Sequence) sseq.getObjectAt(k);
0330: if (((DERObjectIdentifier) seq2.getObjectAt(0)).getId()
0331: .equals(ID_MESSAGE_DIGEST)) {
0332: ASN1Set set = (ASN1Set) seq2.getObjectAt(1);
0333: digestAttr = ((DEROctetString) set.getObjectAt(0))
0334: .getOctets();
0335: break;
0336: }
0337: }
0338: if (digestAttr == null)
0339: throw new SecurityException(
0340: "Authenticated attribute is missing the digest.");
0341: ++next;
0342: }
0343: digestEncryptionAlgorithm = ((DERObjectIdentifier) ((ASN1Sequence) signerInfo
0344: .getObjectAt(next++)).getObjectAt(0)).getId();
0345: digest = ((DEROctetString) signerInfo.getObjectAt(next))
0346: .getOctets();
0347: if (RSAdata != null || digestAttr != null) {
0348: if (provider == null || provider.startsWith("SunPKCS11"))
0349: messageDigest = MessageDigest
0350: .getInstance(getHashAlgorithm());
0351: else
0352: messageDigest = MessageDigest.getInstance(
0353: getHashAlgorithm(), provider);
0354: }
0355: if (provider == null)
0356: sig = Signature.getInstance(getDigestAlgorithm());
0357: else
0358: sig = Signature.getInstance(getDigestAlgorithm(), provider);
0359: sig.initVerify(signCert.getPublicKey());
0360: }
0361:
0362: /**
0363: * Generates a signature.
0364: * @param privKey the private key
0365: * @param certChain the certificate chain
0366: * @param crlList the certificate revocation list
0367: * @param hashAlgorithm the hash algorithm
0368: * @param provider the provider or <code>null</code> for the default provider
0369: * @param hasRSAdata <CODE>true</CODE> if the sub-filter is adbe.pkcs7.sha1
0370: * @throws SecurityException on error
0371: * @throws InvalidKeyException on error
0372: * @throws NoSuchProviderException on error
0373: * @throws NoSuchAlgorithmException on error
0374: */
0375: public PdfPKCS7(PrivateKey privKey, Certificate[] certChain,
0376: CRL[] crlList, String hashAlgorithm, String provider,
0377: boolean hasRSAdata) throws SecurityException,
0378: InvalidKeyException, NoSuchProviderException,
0379: NoSuchAlgorithmException {
0380: this .privKey = privKey;
0381:
0382: if (hashAlgorithm.equals("MD5")) {
0383: digestAlgorithm = ID_MD5;
0384: } else if (hashAlgorithm.equals("MD2")) {
0385: digestAlgorithm = ID_MD2;
0386: } else if (hashAlgorithm.equals("SHA")) {
0387: digestAlgorithm = ID_SHA1;
0388: } else if (hashAlgorithm.equals("SHA1")) {
0389: digestAlgorithm = ID_SHA1;
0390: } else {
0391: throw new NoSuchAlgorithmException(
0392: "Unknown Hash Algorithm " + hashAlgorithm);
0393: }
0394:
0395: version = signerversion = 1;
0396: certs = new ArrayList();
0397: crls = new ArrayList();
0398: digestalgos = new HashSet();
0399: digestalgos.add(digestAlgorithm);
0400:
0401: //
0402: // Copy in the certificates and crls used to sign the private key.
0403: //
0404: signCert = (X509Certificate) certChain[0];
0405: for (int i = 0; i < certChain.length; i++) {
0406: certs.add(certChain[i]);
0407: }
0408:
0409: if (crlList != null) {
0410: for (int i = 0; i < crlList.length; i++) {
0411: crls.add(crlList[i]);
0412: }
0413: }
0414:
0415: if (privKey != null) {
0416: //
0417: // Now we have private key, find out what the digestEncryptionAlgorithm is.
0418: //
0419: digestEncryptionAlgorithm = privKey.getAlgorithm();
0420: if (digestEncryptionAlgorithm.equals("RSA")) {
0421: digestEncryptionAlgorithm = ID_RSA;
0422: } else if (digestEncryptionAlgorithm.equals("DSA")) {
0423: digestEncryptionAlgorithm = ID_DSA;
0424: } else {
0425: throw new NoSuchAlgorithmException(
0426: "Unknown Key Algorithm "
0427: + digestEncryptionAlgorithm);
0428: }
0429: }
0430: if (hasRSAdata) {
0431: RSAdata = new byte[0];
0432: if (provider == null || provider.startsWith("SunPKCS11"))
0433: messageDigest = MessageDigest
0434: .getInstance(getHashAlgorithm());
0435: else
0436: messageDigest = MessageDigest.getInstance(
0437: getHashAlgorithm(), provider);
0438: }
0439:
0440: if (privKey != null) {
0441: if (provider == null)
0442: sig = Signature.getInstance(getDigestAlgorithm());
0443: else
0444: sig = Signature.getInstance(getDigestAlgorithm(),
0445: provider);
0446:
0447: sig.initSign(privKey);
0448: }
0449: }
0450:
0451: /**
0452: * Update the digest with the specified bytes. This method is used both for signing and verifying
0453: * @param buf the data buffer
0454: * @param off the offset in the data buffer
0455: * @param len the data length
0456: * @throws SignatureException on error
0457: */
0458: public void update(byte[] buf, int off, int len)
0459: throws SignatureException {
0460: if (RSAdata != null || digestAttr != null)
0461: messageDigest.update(buf, off, len);
0462: else
0463: sig.update(buf, off, len);
0464: }
0465:
0466: /**
0467: * Verify the digest.
0468: * @throws SignatureException on error
0469: * @return <CODE>true</CODE> if the signature checks out, <CODE>false</CODE> otherwise
0470: */
0471: public boolean verify() throws SignatureException {
0472: if (verified)
0473: return verifyResult;
0474: if (sigAttr != null) {
0475: sig.update(sigAttr);
0476: if (RSAdata != null) {
0477: byte msd[] = messageDigest.digest();
0478: messageDigest.update(msd);
0479: }
0480: verifyResult = (Arrays.equals(messageDigest.digest(),
0481: digestAttr) && sig.verify(digest));
0482: } else {
0483: if (RSAdata != null)
0484: sig.update(messageDigest.digest());
0485: verifyResult = sig.verify(digest);
0486: }
0487: verified = true;
0488: return verifyResult;
0489: }
0490:
0491: /**
0492: * Get the X.509 certificates associated with this PKCS#7 object
0493: * @return the X.509 certificates associated with this PKCS#7 object
0494: */
0495: public Certificate[] getCertificates() {
0496: return (X509Certificate[]) certs
0497: .toArray(new X509Certificate[certs.size()]);
0498: }
0499:
0500: /**
0501: * Get the X.509 certificate revocation lists associated with this PKCS#7 object
0502: * @return the X.509 certificate revocation lists associated with this PKCS#7 object
0503: */
0504: public Collection getCRLs() {
0505: return crls;
0506: }
0507:
0508: /**
0509: * Get the X.509 certificate actually used to sign the digest.
0510: * @return the X.509 certificate actually used to sign the digest
0511: */
0512: public X509Certificate getSigningCertificate() {
0513: return signCert;
0514: }
0515:
0516: /**
0517: * Get the version of the PKCS#7 object. Always 1
0518: * @return the version of the PKCS#7 object. Always 1
0519: */
0520: public int getVersion() {
0521: return version;
0522: }
0523:
0524: /**
0525: * Get the version of the PKCS#7 "SignerInfo" object. Always 1
0526: * @return the version of the PKCS#7 "SignerInfo" object. Always 1
0527: */
0528: public int getSigningInfoVersion() {
0529: return signerversion;
0530: }
0531:
0532: /**
0533: * Get the algorithm used to calculate the message digest
0534: * @return the algorithm used to calculate the message digest
0535: */
0536: public String getDigestAlgorithm() {
0537: String dea = digestEncryptionAlgorithm;
0538:
0539: if (digestEncryptionAlgorithm.equals(ID_RSA)
0540: || digestEncryptionAlgorithm.equals(ID_MD5RSA)
0541: || digestEncryptionAlgorithm.equals(ID_MD2RSA)
0542: || digestEncryptionAlgorithm.equals(ID_SHA1RSA)) {
0543: dea = "RSA";
0544: } else if (digestEncryptionAlgorithm.equals(ID_DSA)) {
0545: dea = "DSA";
0546: }
0547:
0548: return getHashAlgorithm() + "with" + dea;
0549: }
0550:
0551: /**
0552: * Returns the algorithm.
0553: * @return the digest algorithm
0554: */
0555: public String getHashAlgorithm() {
0556: String da = digestAlgorithm;
0557:
0558: if (digestAlgorithm.equals(ID_MD5)
0559: || digestAlgorithm.equals(ID_MD5RSA)) {
0560: da = "MD5";
0561: } else if (digestAlgorithm.equals(ID_MD2)
0562: || digestAlgorithm.equals(ID_MD2RSA)) {
0563: da = "MD2";
0564: } else if (digestAlgorithm.equals(ID_SHA1)
0565: || digestAlgorithm.equals(ID_SHA1RSA)) {
0566: da = "SHA1";
0567: }
0568: return da;
0569: }
0570:
0571: /**
0572: * Loads the default root certificates at <java.home>/lib/security/cacerts
0573: * with the default provider.
0574: * @return a <CODE>KeyStore</CODE>
0575: */
0576: public static KeyStore loadCacertsKeyStore() {
0577: return loadCacertsKeyStore(null);
0578: }
0579:
0580: /**
0581: * Loads the default root certificates at <java.home>/lib/security/cacerts.
0582: * @param provider the provider or <code>null</code> for the default provider
0583: * @return a <CODE>KeyStore</CODE>
0584: */
0585: public static KeyStore loadCacertsKeyStore(String provider) {
0586: File file = new File(System.getProperty("java.home"), "lib");
0587: file = new File(file, "security");
0588: file = new File(file, "cacerts");
0589: FileInputStream fin = null;
0590: try {
0591: fin = new FileInputStream(file);
0592: KeyStore k;
0593: if (provider == null)
0594: k = KeyStore.getInstance("JKS");
0595: else
0596: k = KeyStore.getInstance("JKS", provider);
0597: k.load(fin, null);
0598: return k;
0599: } catch (Exception e) {
0600: throw new ExceptionConverter(e);
0601: } finally {
0602: try {
0603: if (fin != null) {
0604: fin.close();
0605: }
0606: } catch (Exception ex) {
0607: }
0608: }
0609: }
0610:
0611: /**
0612: * Verifies a single certificate.
0613: * @param cert the certificate to verify
0614: * @param crls the certificate revocation list or <CODE>null</CODE>
0615: * @param calendar the date or <CODE>null</CODE> for the current date
0616: * @return a <CODE>String</CODE> with the error description or <CODE>null</CODE>
0617: * if no error
0618: */
0619: public static String verifyCertificate(X509Certificate cert,
0620: Collection crls, Calendar calendar) {
0621: if (calendar == null)
0622: calendar = new GregorianCalendar();
0623: if (cert.hasUnsupportedCriticalExtension())
0624: return "Has unsupported critical extension";
0625: try {
0626: cert.checkValidity(calendar.getTime());
0627: } catch (Exception e) {
0628: return e.getMessage();
0629: }
0630: if (crls != null) {
0631: for (Iterator it = crls.iterator(); it.hasNext();) {
0632: if (((CRL) it.next()).isRevoked(cert))
0633: return "Certificate revoked";
0634: }
0635: }
0636: return null;
0637: }
0638:
0639: /**
0640: * Verifies a certificate chain against a KeyStore.
0641: * @param certs the certificate chain
0642: * @param keystore the <CODE>KeyStore</CODE>
0643: * @param crls the certificate revocation list or <CODE>null</CODE>
0644: * @param calendar the date or <CODE>null</CODE> for the current date
0645: * @return <CODE>null</CODE> if the certificate chain could be validade or a
0646: * <CODE>Object[]{cert,error}</CODE> where <CODE>cert</CODE> is the
0647: * failed certificate and <CODE>error</CODE> is the error message
0648: */
0649: public static Object[] verifyCertificates(Certificate certs[],
0650: KeyStore keystore, Collection crls, Calendar calendar) {
0651: if (calendar == null)
0652: calendar = new GregorianCalendar();
0653: for (int k = 0; k < certs.length; ++k) {
0654: X509Certificate cert = (X509Certificate) certs[k];
0655: String err = verifyCertificate(cert, crls, calendar);
0656: if (err != null)
0657: return new Object[] { cert, err };
0658: try {
0659: for (Enumeration aliases = keystore.aliases(); aliases
0660: .hasMoreElements();) {
0661: try {
0662: String alias = (String) aliases.nextElement();
0663: if (!keystore.isCertificateEntry(alias))
0664: continue;
0665: X509Certificate certStoreX509 = (X509Certificate) keystore
0666: .getCertificate(alias);
0667: if (verifyCertificate(certStoreX509, crls,
0668: calendar) != null)
0669: continue;
0670: try {
0671: cert.verify(certStoreX509.getPublicKey());
0672: return null;
0673: } catch (Exception e) {
0674: continue;
0675: }
0676: } catch (Exception ex) {
0677: }
0678: }
0679: } catch (Exception e) {
0680: }
0681: int j;
0682: for (j = 0; j < certs.length; ++j) {
0683: if (j == k)
0684: continue;
0685: X509Certificate certNext = (X509Certificate) certs[j];
0686: try {
0687: cert.verify(certNext.getPublicKey());
0688: break;
0689: } catch (Exception e) {
0690: }
0691: }
0692: if (j == certs.length)
0693: return new Object[] { cert,
0694: "Cannot be verified against the KeyStore or the certificate chain" };
0695: }
0696: return new Object[] { null,
0697: "Invalid state. Possible circular certificate chain" };
0698: }
0699:
0700: /**
0701: * Get the "issuer" from the TBSCertificate bytes that are passed in
0702: * @param enc a TBSCertificate in a byte array
0703: * @return a DERObject
0704: */
0705: private static DERObject getIssuer(byte[] enc) {
0706: try {
0707: ASN1InputStream in = new ASN1InputStream(
0708: new ByteArrayInputStream(enc));
0709: ASN1Sequence seq = (ASN1Sequence) in.readObject();
0710: return (DERObject) seq
0711: .getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3
0712: : 2);
0713: } catch (IOException e) {
0714: throw new ExceptionConverter(e);
0715: }
0716: }
0717:
0718: /**
0719: * Get the "subject" from the TBSCertificate bytes that are passed in
0720: * @param enc A TBSCertificate in a byte array
0721: * @return a DERObject
0722: */
0723: private static DERObject getSubject(byte[] enc) {
0724: try {
0725: ASN1InputStream in = new ASN1InputStream(
0726: new ByteArrayInputStream(enc));
0727: ASN1Sequence seq = (ASN1Sequence) in.readObject();
0728: return (DERObject) seq
0729: .getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 5
0730: : 4);
0731: } catch (IOException e) {
0732: throw new ExceptionConverter(e);
0733: }
0734: }
0735:
0736: /**
0737: * Get the issuer fields from an X509 Certificate
0738: * @param cert an X509Certificate
0739: * @return an X509Name
0740: */
0741: public static X509Name getIssuerFields(X509Certificate cert) {
0742: try {
0743: return new X509Name((ASN1Sequence) getIssuer(cert
0744: .getTBSCertificate()));
0745: } catch (Exception e) {
0746: throw new ExceptionConverter(e);
0747: }
0748: }
0749:
0750: /**
0751: * Get the subject fields from an X509 Certificate
0752: * @param cert an X509Certificate
0753: * @return an X509Name
0754: */
0755: public static X509Name getSubjectFields(X509Certificate cert) {
0756: try {
0757: return new X509Name((ASN1Sequence) getSubject(cert
0758: .getTBSCertificate()));
0759: } catch (Exception e) {
0760: throw new ExceptionConverter(e);
0761: }
0762: }
0763:
0764: /**
0765: * Gets the bytes for the PKCS#1 object.
0766: * @return a byte array
0767: */
0768: public byte[] getEncodedPKCS1() {
0769: try {
0770: if (externalDigest != null)
0771: digest = externalDigest;
0772: else
0773: digest = sig.sign();
0774: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
0775:
0776: ASN1OutputStream dout = new ASN1OutputStream(bOut);
0777: dout.writeObject(new DEROctetString(digest));
0778: dout.close();
0779:
0780: return bOut.toByteArray();
0781: } catch (Exception e) {
0782: throw new ExceptionConverter(e);
0783: }
0784: }
0785:
0786: /**
0787: * Sets the digest/signature to an external calculated value.
0788: * @param digest the digest. This is the actual signature
0789: * @param RSAdata the extra data that goes into the data tag in PKCS#7
0790: * @param digestEncryptionAlgorithm the encryption algorithm. It may must be <CODE>null</CODE> if the <CODE>digest</CODE>
0791: * is also <CODE>null</CODE>. If the <CODE>digest</CODE> is not <CODE>null</CODE>
0792: * then it may be "RSA" or "DSA"
0793: */
0794: public void setExternalDigest(byte digest[], byte RSAdata[],
0795: String digestEncryptionAlgorithm) {
0796: externalDigest = digest;
0797: externalRSAdata = RSAdata;
0798: if (digestEncryptionAlgorithm != null) {
0799: if (digestEncryptionAlgorithm.equals("RSA")) {
0800: this .digestEncryptionAlgorithm = ID_RSA;
0801: } else if (digestEncryptionAlgorithm.equals("DSA")) {
0802: this .digestEncryptionAlgorithm = ID_DSA;
0803: } else
0804: throw new ExceptionConverter(
0805: new NoSuchAlgorithmException(
0806: "Unknown Key Algorithm "
0807: + digestEncryptionAlgorithm));
0808: }
0809: }
0810:
0811: /**
0812: * Gets the bytes for the PKCS7SignedData object.
0813: * @return the bytes for the PKCS7SignedData object
0814: */
0815: public byte[] getEncodedPKCS7() {
0816: return getEncodedPKCS7(null, null);
0817: }
0818:
0819: /**
0820: * Gets the bytes for the PKCS7SignedData object. Optionally the authenticatedAttributes
0821: * in the signerInfo can also be set. If either of the parameters is <CODE>null</CODE>, none will be used.
0822: * @param secondDigest the digest in the authenticatedAttributes
0823: * @param signingTime the signing time in the authenticatedAttributes
0824: * @return the bytes for the PKCS7SignedData object
0825: */
0826: public byte[] getEncodedPKCS7(byte secondDigest[],
0827: Calendar signingTime) {
0828: try {
0829: if (externalDigest != null) {
0830: digest = externalDigest;
0831: if (RSAdata != null)
0832: RSAdata = externalRSAdata;
0833: } else if (externalRSAdata != null && RSAdata != null) {
0834: RSAdata = externalRSAdata;
0835: sig.update(RSAdata);
0836: digest = sig.sign();
0837: } else {
0838: if (RSAdata != null) {
0839: RSAdata = messageDigest.digest();
0840: sig.update(RSAdata);
0841: }
0842: digest = sig.sign();
0843: }
0844:
0845: // Create the set of Hash algorithms
0846: DERConstructedSet digestAlgorithms = new DERConstructedSet();
0847: for (Iterator it = digestalgos.iterator(); it.hasNext();) {
0848: ASN1EncodableVector algos = new ASN1EncodableVector();
0849: algos.add(new DERObjectIdentifier((String) it.next()));
0850: algos.add(new DERNull());
0851: digestAlgorithms.addObject(new DERSequence(algos));
0852: }
0853:
0854: // Create the contentInfo.
0855: ASN1EncodableVector v = new ASN1EncodableVector();
0856: v.add(new DERObjectIdentifier(ID_PKCS7_DATA));
0857: if (RSAdata != null)
0858: v.add(new DERTaggedObject(0,
0859: new DEROctetString(RSAdata)));
0860: DERSequence contentinfo = new DERSequence(v);
0861:
0862: // Get all the certificates
0863: //
0864: v = new ASN1EncodableVector();
0865: for (Iterator i = certs.iterator(); i.hasNext();) {
0866: ASN1InputStream tempstream = new ASN1InputStream(
0867: new ByteArrayInputStream(((X509Certificate) i
0868: .next()).getEncoded()));
0869: v.add(tempstream.readObject());
0870: }
0871:
0872: DERSet dercertificates = new DERSet(v);
0873:
0874: // Create signerinfo structure.
0875: //
0876: ASN1EncodableVector signerinfo = new ASN1EncodableVector();
0877:
0878: // Add the signerInfo version
0879: //
0880: signerinfo.add(new DERInteger(signerversion));
0881:
0882: v = new ASN1EncodableVector();
0883: v.add(getIssuer(signCert.getTBSCertificate()));
0884: v.add(new DERInteger(signCert.getSerialNumber()));
0885: signerinfo.add(new DERSequence(v));
0886:
0887: // Add the digestAlgorithm
0888: v = new ASN1EncodableVector();
0889: v.add(new DERObjectIdentifier(digestAlgorithm));
0890: v.add(new DERNull());
0891: signerinfo.add(new DERSequence(v));
0892:
0893: // add the authenticated attribute if present
0894: if (secondDigest != null && signingTime != null) {
0895: ASN1EncodableVector attribute = new ASN1EncodableVector();
0896: v = new ASN1EncodableVector();
0897: v.add(new DERObjectIdentifier(ID_CONTENT_TYPE));
0898: v
0899: .add(new DERSet(new DERObjectIdentifier(
0900: ID_PKCS7_DATA)));
0901: attribute.add(new DERSequence(v));
0902: v = new ASN1EncodableVector();
0903: v.add(new DERObjectIdentifier(ID_SIGNING_TIME));
0904: v
0905: .add(new DERSet(new DERUTCTime(signingTime
0906: .getTime())));
0907: attribute.add(new DERSequence(v));
0908: v = new ASN1EncodableVector();
0909: v.add(new DERObjectIdentifier(ID_MESSAGE_DIGEST));
0910: v.add(new DERSet(new DEROctetString(secondDigest)));
0911: attribute.add(new DERSequence(v));
0912: if (!crls.isEmpty()) {
0913: v = new ASN1EncodableVector();
0914: v.add(new DERObjectIdentifier(ID_ADBE_REVOCATION));
0915: ASN1EncodableVector v2 = new ASN1EncodableVector();
0916: for (Iterator i = crls.iterator(); i.hasNext();) {
0917: ASN1InputStream t = new ASN1InputStream(
0918: new ByteArrayInputStream((((X509CRL) i
0919: .next()).getEncoded())));
0920: v2.add(t.readObject());
0921: }
0922: v.add(new DERSet(new DERSequence(
0923: new DERTaggedObject(true, 0,
0924: new DERSequence(v2)))));
0925: attribute.add(new DERSequence(v));
0926: }
0927: signerinfo.add(new DERTaggedObject(false, 0,
0928: new DERSet(attribute)));
0929: }
0930: // Add the digestEncryptionAlgorithm
0931: v = new ASN1EncodableVector();
0932: v.add(new DERObjectIdentifier(digestEncryptionAlgorithm));
0933: v.add(new DERNull());
0934: signerinfo.add(new DERSequence(v));
0935:
0936: // Add the digest
0937: signerinfo.add(new DEROctetString(digest));
0938:
0939: // Finally build the body out of all the components above
0940: ASN1EncodableVector body = new ASN1EncodableVector();
0941: body.add(new DERInteger(version));
0942: body.add(digestAlgorithms);
0943: body.add(contentinfo);
0944: body.add(new DERTaggedObject(false, 0, dercertificates));
0945:
0946: if (!crls.isEmpty()) {
0947: v = new ASN1EncodableVector();
0948: for (Iterator i = crls.iterator(); i.hasNext();) {
0949: ASN1InputStream t = new ASN1InputStream(
0950: new ByteArrayInputStream((((X509CRL) i
0951: .next()).getEncoded())));
0952: v.add(t.readObject());
0953: }
0954: DERSet dercrls = new DERSet(v);
0955: body.add(new DERTaggedObject(false, 1, dercrls));
0956: }
0957:
0958: // Only allow one signerInfo
0959: body.add(new DERSet(new DERSequence(signerinfo)));
0960:
0961: // Now we have the body, wrap it in it's PKCS7Signed shell
0962: // and return it
0963: //
0964: ASN1EncodableVector whole = new ASN1EncodableVector();
0965: whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA));
0966: whole.add(new DERTaggedObject(0, new DERSequence(body)));
0967:
0968: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
0969:
0970: ASN1OutputStream dout = new ASN1OutputStream(bOut);
0971: dout.writeObject(new DERSequence(whole));
0972: dout.close();
0973:
0974: return bOut.toByteArray();
0975: } catch (Exception e) {
0976: throw new ExceptionConverter(e);
0977: }
0978: }
0979:
0980: /**
0981: * When using authenticatedAttributes the authentication process is different.
0982: * The document digest is generated and put inside the attribute. The signing is done over the DER encoded
0983: * authenticatedAttributes. This method provides that encoding and the parameters must be
0984: * exactly the same as in {@link #getEncodedPKCS7(byte[],Calendar)}.
0985: * <p>
0986: * A simple example:
0987: * <p>
0988: * <pre>
0989: * Calendar cal = Calendar.getInstance();
0990: * PdfPKCS7 pk7 = new PdfPKCS7(key, chain, null, "SHA1", null, false);
0991: * MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
0992: * byte buf[] = new byte[8192];
0993: * int n;
0994: * InputStream inp = sap.getRangeStream();
0995: * while ((n = inp.read(buf)) > 0) {
0996: * messageDigest.update(buf, 0, n);
0997: * }
0998: * byte hash[] = messageDigest.digest();
0999: * byte sh[] = pk7.getAuthenticatedAttributeBytes(hash, cal);
1000: * pk7.update(sh, 0, sh.length);
1001: * byte sg[] = pk7.getEncodedPKCS7(hash, cal);
1002: * </pre>
1003: * @param secondDigest the content digest
1004: * @param signingTime the signing time
1005: * @return the byte array representation of the authenticatedAttributes ready to be signed
1006: */
1007: public byte[] getAuthenticatedAttributeBytes(byte secondDigest[],
1008: Calendar signingTime) {
1009: try {
1010: ASN1EncodableVector attribute = new ASN1EncodableVector();
1011: ASN1EncodableVector v = new ASN1EncodableVector();
1012: v.add(new DERObjectIdentifier(ID_CONTENT_TYPE));
1013: v.add(new DERSet(new DERObjectIdentifier(ID_PKCS7_DATA)));
1014: attribute.add(new DERSequence(v));
1015: v = new ASN1EncodableVector();
1016: v.add(new DERObjectIdentifier(ID_SIGNING_TIME));
1017: v.add(new DERSet(new DERUTCTime(signingTime.getTime())));
1018: attribute.add(new DERSequence(v));
1019: v = new ASN1EncodableVector();
1020: v.add(new DERObjectIdentifier(ID_MESSAGE_DIGEST));
1021: v.add(new DERSet(new DEROctetString(secondDigest)));
1022: attribute.add(new DERSequence(v));
1023: if (!crls.isEmpty()) {
1024: v = new ASN1EncodableVector();
1025: v.add(new DERObjectIdentifier(ID_ADBE_REVOCATION));
1026: ASN1EncodableVector v2 = new ASN1EncodableVector();
1027: for (Iterator i = crls.iterator(); i.hasNext();) {
1028: ASN1InputStream t = new ASN1InputStream(
1029: new ByteArrayInputStream((((X509CRL) i
1030: .next()).getEncoded())));
1031: v2.add(t.readObject());
1032: }
1033: v.add(new DERSet(new DERSequence(new DERTaggedObject(
1034: true, 0, new DERSequence(v2)))));
1035: attribute.add(new DERSequence(v));
1036: }
1037: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
1038:
1039: ASN1OutputStream dout = new ASN1OutputStream(bOut);
1040: dout.writeObject(new DERSet(attribute));
1041: dout.close();
1042:
1043: return bOut.toByteArray();
1044: } catch (Exception e) {
1045: throw new ExceptionConverter(e);
1046: }
1047: }
1048:
1049: /**
1050: * Getter for property reason.
1051: * @return Value of property reason.
1052: */
1053: public String getReason() {
1054: return this .reason;
1055: }
1056:
1057: /**
1058: * Setter for property reason.
1059: * @param reason New value of property reason.
1060: */
1061: public void setReason(String reason) {
1062: this .reason = reason;
1063: }
1064:
1065: /**
1066: * Getter for property location.
1067: * @return Value of property location.
1068: */
1069: public String getLocation() {
1070: return this .location;
1071: }
1072:
1073: /**
1074: * Setter for property location.
1075: * @param location New value of property location.
1076: */
1077: public void setLocation(String location) {
1078: this .location = location;
1079: }
1080:
1081: /**
1082: * Getter for property signDate.
1083: * @return Value of property signDate.
1084: */
1085: public Calendar getSignDate() {
1086: return this .signDate;
1087: }
1088:
1089: /**
1090: * Setter for property signDate.
1091: * @param signDate New value of property signDate.
1092: */
1093: public void setSignDate(Calendar signDate) {
1094: this .signDate = signDate;
1095: }
1096:
1097: /**
1098: * Getter for property sigName.
1099: * @return Value of property sigName.
1100: */
1101: public String getSignName() {
1102: return this .signName;
1103: }
1104:
1105: /**
1106: * Setter for property sigName.
1107: * @param signName New value of property sigName.
1108: */
1109: public void setSignName(String signName) {
1110: this .signName = signName;
1111: }
1112:
1113: /**
1114: * a class that holds an X509 name
1115: */
1116: public static class X509Name {
1117: /**
1118: * country code - StringType(SIZE(2))
1119: */
1120: public static final DERObjectIdentifier C = new DERObjectIdentifier(
1121: "2.5.4.6");
1122:
1123: /**
1124: * organization - StringType(SIZE(1..64))
1125: */
1126: public static final DERObjectIdentifier O = new DERObjectIdentifier(
1127: "2.5.4.10");
1128:
1129: /**
1130: * organizational unit name - StringType(SIZE(1..64))
1131: */
1132: public static final DERObjectIdentifier OU = new DERObjectIdentifier(
1133: "2.5.4.11");
1134:
1135: /**
1136: * Title
1137: */
1138: public static final DERObjectIdentifier T = new DERObjectIdentifier(
1139: "2.5.4.12");
1140:
1141: /**
1142: * common name - StringType(SIZE(1..64))
1143: */
1144: public static final DERObjectIdentifier CN = new DERObjectIdentifier(
1145: "2.5.4.3");
1146:
1147: /**
1148: * device serial number name - StringType(SIZE(1..64))
1149: */
1150: public static final DERObjectIdentifier SN = new DERObjectIdentifier(
1151: "2.5.4.5");
1152:
1153: /**
1154: * locality name - StringType(SIZE(1..64))
1155: */
1156: public static final DERObjectIdentifier L = new DERObjectIdentifier(
1157: "2.5.4.7");
1158:
1159: /**
1160: * state, or province name - StringType(SIZE(1..64))
1161: */
1162: public static final DERObjectIdentifier ST = new DERObjectIdentifier(
1163: "2.5.4.8");
1164:
1165: /** Naming attribute of type X520name */
1166: public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier(
1167: "2.5.4.4");
1168: /** Naming attribute of type X520name */
1169: public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier(
1170: "2.5.4.42");
1171: /** Naming attribute of type X520name */
1172: public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier(
1173: "2.5.4.43");
1174: /** Naming attribute of type X520name */
1175: public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier(
1176: "2.5.4.44");
1177: /** Naming attribute of type X520name */
1178: public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier(
1179: "2.5.4.45");
1180:
1181: /**
1182: * Email address (RSA PKCS#9 extension) - IA5String.
1183: * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
1184: */
1185: public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier(
1186: "1.2.840.113549.1.9.1");
1187:
1188: /**
1189: * email address in Verisign certificates
1190: */
1191: public static final DERObjectIdentifier E = EmailAddress;
1192:
1193: /** object identifier */
1194: public static final DERObjectIdentifier DC = new DERObjectIdentifier(
1195: "0.9.2342.19200300.100.1.25");
1196:
1197: /** LDAP User id. */
1198: public static final DERObjectIdentifier UID = new DERObjectIdentifier(
1199: "0.9.2342.19200300.100.1.1");
1200:
1201: /** A HashMap with default symbols */
1202: public static HashMap DefaultSymbols = new HashMap();
1203:
1204: static {
1205: DefaultSymbols.put(C, "C");
1206: DefaultSymbols.put(O, "O");
1207: DefaultSymbols.put(T, "T");
1208: DefaultSymbols.put(OU, "OU");
1209: DefaultSymbols.put(CN, "CN");
1210: DefaultSymbols.put(L, "L");
1211: DefaultSymbols.put(ST, "ST");
1212: DefaultSymbols.put(SN, "SN");
1213: DefaultSymbols.put(EmailAddress, "E");
1214: DefaultSymbols.put(DC, "DC");
1215: DefaultSymbols.put(UID, "UID");
1216: DefaultSymbols.put(SURNAME, "SURNAME");
1217: DefaultSymbols.put(GIVENNAME, "GIVENNAME");
1218: DefaultSymbols.put(INITIALS, "INITIALS");
1219: DefaultSymbols.put(GENERATION, "GENERATION");
1220: }
1221: /** A HashMap with values */
1222: public HashMap values = new HashMap();
1223:
1224: /**
1225: * Constructs an X509 name
1226: * @param seq an ASN1 Sequence
1227: */
1228: public X509Name(ASN1Sequence seq) {
1229: Enumeration e = seq.getObjects();
1230:
1231: while (e.hasMoreElements()) {
1232: ASN1Set set = (ASN1Set) e.nextElement();
1233:
1234: for (int i = 0; i < set.size(); i++) {
1235: ASN1Sequence s = (ASN1Sequence) set.getObjectAt(i);
1236: String id = (String) DefaultSymbols.get(s
1237: .getObjectAt(0));
1238: if (id == null)
1239: continue;
1240: ArrayList vs = (ArrayList) values.get(id);
1241: if (vs == null) {
1242: vs = new ArrayList();
1243: values.put(id, vs);
1244: }
1245: vs.add(((DERString) s.getObjectAt(1)).getString());
1246: }
1247: }
1248: }
1249:
1250: /**
1251: * Constructs an X509 name
1252: * @param dirName a directory name
1253: */
1254: public X509Name(String dirName) {
1255: X509NameTokenizer nTok = new X509NameTokenizer(dirName);
1256:
1257: while (nTok.hasMoreTokens()) {
1258: String token = nTok.nextToken();
1259: int index = token.indexOf('=');
1260:
1261: if (index == -1) {
1262: throw new IllegalArgumentException(
1263: "badly formated directory string");
1264: }
1265:
1266: String id = token.substring(0, index).toUpperCase();
1267: String value = token.substring(index + 1);
1268: ArrayList vs = (ArrayList) values.get(id);
1269: if (vs == null) {
1270: vs = new ArrayList();
1271: values.put(id, vs);
1272: }
1273: vs.add(value);
1274: }
1275:
1276: }
1277:
1278: public String getField(String name) {
1279: ArrayList vs = (ArrayList) values.get(name);
1280: return vs == null ? null : (String) vs.get(0);
1281: }
1282:
1283: /**
1284: * gets a field array from the values Hashmap
1285: * @param name
1286: * @return an ArrayList
1287: */
1288: public ArrayList getFieldArray(String name) {
1289: ArrayList vs = (ArrayList) values.get(name);
1290: return vs == null ? null : vs;
1291: }
1292:
1293: /**
1294: * getter for values
1295: * @return a HashMap with the fields of the X509 name
1296: */
1297: public HashMap getFields() {
1298: return values;
1299: }
1300:
1301: /**
1302: * @see java.lang.Object#toString()
1303: */
1304: public String toString() {
1305: return values.toString();
1306: }
1307: }
1308:
1309: /**
1310: * class for breaking up an X500 Name into it's component tokens, ala
1311: * java.util.StringTokenizer. We need this class as some of the
1312: * lightweight Java environment don't support classes like
1313: * StringTokenizer.
1314: */
1315: public static class X509NameTokenizer {
1316: private String oid;
1317: private int index;
1318: private StringBuffer buf = new StringBuffer();
1319:
1320: public X509NameTokenizer(String oid) {
1321: this .oid = oid;
1322: this .index = -1;
1323: }
1324:
1325: public boolean hasMoreTokens() {
1326: return (index != oid.length());
1327: }
1328:
1329: public String nextToken() {
1330: if (index == oid.length()) {
1331: return null;
1332: }
1333:
1334: int end = index + 1;
1335: boolean quoted = false;
1336: boolean escaped = false;
1337:
1338: buf.setLength(0);
1339:
1340: while (end != oid.length()) {
1341: char c = oid.charAt(end);
1342:
1343: if (c == '"') {
1344: if (!escaped) {
1345: quoted = !quoted;
1346: } else {
1347: buf.append(c);
1348: }
1349: escaped = false;
1350: } else {
1351: if (escaped || quoted) {
1352: buf.append(c);
1353: escaped = false;
1354: } else if (c == '\\') {
1355: escaped = true;
1356: } else if (c == ',') {
1357: break;
1358: } else {
1359: buf.append(c);
1360: }
1361: }
1362: end++;
1363: }
1364:
1365: index = end;
1366: return buf.toString().trim();
1367: }
1368: }
1369: }
|