summaryrefslogtreecommitdiffstats
path: root/base/util/src/netscape/security/pkcs
diff options
context:
space:
mode:
Diffstat (limited to 'base/util/src/netscape/security/pkcs')
-rw-r--r--base/util/src/netscape/security/pkcs/ContentInfo.java155
-rw-r--r--base/util/src/netscape/security/pkcs/EncodingException.java33
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS10.java343
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS10Attribute.java238
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS10Attributes.java147
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS7.java446
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS8Key.java435
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS9Attribute.java1123
-rw-r--r--base/util/src/netscape/security/pkcs/PKCS9Attributes.java312
-rw-r--r--base/util/src/netscape/security/pkcs/ParsingException.java35
-rw-r--r--base/util/src/netscape/security/pkcs/SignerInfo.java347
11 files changed, 3614 insertions, 0 deletions
diff --git a/base/util/src/netscape/security/pkcs/ContentInfo.java b/base/util/src/netscape/security/pkcs/ContentInfo.java
new file mode 100644
index 000000000..9825494c2
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/ContentInfo.java
@@ -0,0 +1,155 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+
+/**
+ * A ContentInfo type, as defined in PKCS#7.
+ *
+ * @version 1.12
+ * @author Benjamin Renaud
+ */
+
+public class ContentInfo {
+
+ // pkcs7 pre-defined content types
+ private static int[] pkcs7 = { 1, 2, 840, 113549, 1, 7 };
+ private static int[] data = { 1, 2, 840, 113549, 1, 7, 1 };
+ private static int[] sdata = { 1, 2, 840, 113549, 1, 7, 2 };
+ private static int[] edata = { 1, 2, 840, 113549, 1, 7, 3 };
+ private static int[] sedata = { 1, 2, 840, 113549, 1, 7, 4 };
+ private static int[] ddata = { 1, 2, 840, 113549, 1, 7, 5 };
+ private static int[] crdata = { 1, 2, 840, 113549, 1, 7, 6 };
+
+ public static final ObjectIdentifier PKCS7_OID =
+ new ObjectIdentifier(pkcs7);
+
+ public static final ObjectIdentifier DATA_OID =
+ new ObjectIdentifier(data);
+
+ public static final ObjectIdentifier SIGNED_DATA_OID =
+ new ObjectIdentifier(sdata);
+
+ public static final ObjectIdentifier ENVELOPED_DATA_OID =
+ new ObjectIdentifier(edata);
+
+ public static final ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID =
+ new ObjectIdentifier(sedata);
+
+ public static final ObjectIdentifier DIGESTED_DATA_OID =
+ new ObjectIdentifier(ddata);
+
+ public static final ObjectIdentifier ENCRYPTED_DATA_OID =
+ new ObjectIdentifier(crdata);
+
+ ObjectIdentifier contentType;
+ DerValue content; // OPTIONAL
+
+ public ContentInfo(ObjectIdentifier contentType, DerValue content) {
+ this.contentType = contentType;
+ this.content = content;
+ }
+
+ /**
+ * Make a contentInfo of type data.
+ */
+ public ContentInfo(byte[] bytes) {
+ DerValue octetString = new DerValue(DerValue.tag_OctetString, bytes);
+ this.contentType = DATA_OID;
+ this.content = octetString;
+ }
+
+ public ContentInfo(DerInputStream derin)
+ throws IOException, ParsingException {
+ DerInputStream disType;
+ DerInputStream disTaggedContent;
+ DerValue type;
+ DerValue taggedContent;
+ DerValue[] typeAndContent;
+ DerValue[] contents;
+
+ typeAndContent = derin.getSequence(2);
+
+ // Parse the content type
+ type = typeAndContent[0];
+ disType = new DerInputStream(type.toByteArray());
+ contentType = disType.getOID();
+
+ // Parse the content (OPTIONAL field).
+ // Skip the [0] EXPLICIT tag by pretending that the content is the one
+ // and only element in an implicitly tagged set
+ if (typeAndContent.length > 1) { // content is OPTIONAL
+ taggedContent = typeAndContent[1];
+ disTaggedContent = new DerInputStream(taggedContent.toByteArray());
+ contents = disTaggedContent.getSet(1, true);
+ content = contents[0];
+ }
+ }
+
+ public DerValue getContent() {
+ return content;
+ }
+
+ public byte[] getData() throws IOException {
+ if (contentType.equals(DATA_OID)) {
+ return content.getOctetString();
+ }
+ throw new IOException("content type is not DATA: " + contentType);
+ }
+
+ public void encode(DerOutputStream out) throws IOException {
+ DerOutputStream contentDerCode;
+ DerOutputStream seq;
+ DerValue taggedContent;
+
+ contentDerCode = new DerOutputStream();
+ content.encode(contentDerCode);
+ // Add the [0] EXPLICIT tag in front of the content encoding
+ taggedContent = new DerValue((byte) 0xA0,
+ contentDerCode.toByteArray());
+
+ seq = new DerOutputStream();
+ seq.putOID(contentType);
+ seq.putDerValue(taggedContent);
+
+ out.write(DerValue.tag_Sequence, seq);
+ }
+
+ /**
+ * Returns a byte array representation of the data held in
+ * the content field.
+ */
+ public byte[] getContentBytes() throws IOException {
+ DerInputStream dis = new DerInputStream(content.toByteArray());
+ return dis.getOctetString();
+ }
+
+ public String toString() {
+ String out = "";
+
+ out += "Content Info Sequence\n\tContent type: " + contentType + "\n";
+ out += "\tContent: " + content;
+ return out;
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/EncodingException.java b/base/util/src/netscape/security/pkcs/EncodingException.java
new file mode 100644
index 000000000..cb495e990
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/EncodingException.java
@@ -0,0 +1,33 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+public class EncodingException extends Exception {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6126764125859196917L;
+
+ public EncodingException() {
+ super();
+ }
+
+ public EncodingException(String s) {
+ super(s);
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS10.java b/base/util/src/netscape/security/pkcs/PKCS10.java
new file mode 100644
index 000000000..a6ddd203f
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS10.java
@@ -0,0 +1,343 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+
+import com.netscape.cmsutil.util.Utils;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X500Signer;
+import netscape.security.x509.X509Key;
+
+/**
+ * PKCS #10 certificate requests are created and sent to Certificate
+ * Authorities, which then create X.509 certificates and return them to
+ * the entity which created the certificate request. These cert requests
+ * basically consist of the subject's X.500 name and public key, signed
+ * using the corresponding private key.
+ *
+ * The ASN.1 syntax for a Certification Request is:
+ *
+ * <pre>
+ * CertificationRequest ::= SEQUENCE {
+ * certificationRequestInfo CertificationRequestInfo,
+ * signatureAlgorithm SignatureAlgorithmIdentifier,
+ * signature Signature
+ * }
+ *
+ * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
+ * Signature ::= BIT STRING
+ *
+ * CertificationRequestInfo ::= SEQUENCE {
+ * version Version,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * attributes [0] IMPLICIT Attributes
+ * }
+ * Attributes ::= SET OF Attribute
+ * </pre>
+ *
+ * @author David Brownell
+ * @author Amit Kapoor
+ * @author Hemma Prafullchandra
+ * @version 1.28
+ */
+public class PKCS10 {
+ /**
+ * Constructs an unsigned PKCS #10 certificate request. Before this
+ * request may be used, it must be encoded and signed. Then it
+ * must be retrieved in some conventional format (e.g. string).
+ *
+ * @param publicKey the public key that should be placed
+ * into the certificate generated by the CA.
+ */
+ public PKCS10(X509Key publicKey) {
+ subjectPublicKeyInfo = publicKey;
+ attributeSet = new PKCS10Attributes();
+ }
+
+ /**
+ * Constructs an unsigned PKCS #10 certificate request. Before this
+ * request may be used, it must be encoded and signed. Then it
+ * must be retrieved in some conventional format (e.g. string).
+ *
+ * @param publicKey the public key that should be placed
+ * into the certificate generated by the CA.
+ * @param attributes additonal set of PKCS10 attributes requested
+ * for in the certificate.
+ */
+ public PKCS10(X509Key publicKey, PKCS10Attributes attributes) {
+ subjectPublicKeyInfo = publicKey;
+ if (attributes != null)
+ attributeSet = attributes;
+ else
+ attributeSet = new PKCS10Attributes();
+ }
+
+ /**
+ * Parses an encoded, signed PKCS #10 certificate request, verifying
+ * the request's signature as it does so. This constructor would
+ * typically be used by a Certificate Authority, from which a new
+ * certificate would then be constructed.
+ *
+ * @param data the DER-encoded PKCS #10 request.
+ * @param sigver boolean specifies signature verification enabled or not
+ * @exception IOException for low level errors reading the data
+ * @exception SignatureException when the signature is invalid
+ * @exception NoSuchAlgorithmException when the signature
+ * algorithm is not supported in this environment
+ */
+ public PKCS10(byte data[], boolean sigver)
+ throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException {
+ DerInputStream in;
+ DerValue seq[];
+ AlgorithmId id;
+ byte sigData[];
+ Signature sig;
+
+ certificateRequest = data;
+
+ //
+ // Outer sequence: request, signature algorithm, signature.
+ // Parse, and prepare to verify later.
+ //
+ in = new DerInputStream(data);
+ seq = in.getSequence(3);
+
+ if (seq.length != 3)
+ throw new IllegalArgumentException("not a PKCS #10 request");
+
+ data = seq[0].toByteArray(); // reusing this variable
+ certRequestInfo = seq[0].toByteArray(); // make a copy
+ id = AlgorithmId.parse(seq[1]);
+ sigData = seq[2].getBitString();
+
+ //
+ // Inner sequence: version, name, key, attributes
+ //
+ @SuppressWarnings("unused")
+ BigInt serial = seq[0].data.getInteger(); // consume serial
+
+ /*
+ if (serial.toInt () != 0)
+ throw new IllegalArgumentException ("not PKCS #10 v1");
+ */
+
+ subject = new X500Name(seq[0].data);
+
+ byte val1[] = seq[0].data.getDerValue().toByteArray();
+ subjectPublicKeyInfo = X509Key.parse(new DerValue(val1));
+ PublicKey publicKey = X509Key.parsePublicKey(new DerValue(val1));
+
+ // Cope with a somewhat common illegal PKCS #10 format
+ if (seq[0].data.available() != 0)
+ attributeSet = new PKCS10Attributes(seq[0].data);
+ else
+ attributeSet = new PKCS10Attributes();
+
+ //
+ // OK, we parsed it all ... validate the signature using the
+ // key and signature algorithm we found.
+ // temporary commented out
+ try {
+ String idName = id.getName();
+ if (idName.equals("MD5withRSA"))
+ idName = "MD5/RSA";
+ else if (idName.equals("MD2withRSA"))
+ idName = "MD2/RSA";
+ else if (idName.equals("SHA1withRSA"))
+ idName = "SHA1/RSA";
+ else if (idName.equals("SHA1withDSA"))
+ idName = "SHA1/DSA";
+ else if (idName.equals("SHA1withEC"))
+ idName = "SHA1/EC";
+ else if (idName.equals("SHA256withEC"))
+ idName = "SHA256/EC";
+ else if (idName.equals("SHA384withEC"))
+ idName = "SHA384/EC";
+ else if (idName.equals("SHA512withEC"))
+ idName = "SHA512/EC";
+
+ if (sigver) {
+ sig = Signature.getInstance(idName, "Mozilla-JSS");
+
+ sig.initVerify(publicKey);
+ sig.update(data);
+ if (!sig.verify(sigData))
+ throw new SignatureException("Invalid PKCS #10 signature");
+ }
+ } catch (InvalidKeyException e) {
+ throw new SignatureException("invalid key");
+ }
+ }
+
+ public PKCS10(byte data[])
+ throws IOException, SignatureException, NoSuchAlgorithmException, java.security.NoSuchProviderException {
+ this(data, true);
+ }
+
+ /**
+ * Create the signed certificate request. This will later be
+ * retrieved in either string or binary format.
+ *
+ * @param requester identifies the signer (by X.500 name)
+ * and provides the private key used to sign.
+ * @exception IOException on errors.
+ * @exception CertificateException on certificate handling errors.
+ * @exception SignatureException on signature handling errors.
+ */
+ public void encodeAndSign(X500Signer requester)
+ throws CertificateException, IOException, SignatureException {
+ DerOutputStream out, scratch;
+ byte certificateRequestInfo[];
+ byte sig[];
+
+ if (certificateRequest != null)
+ throw new SignatureException("request is already signed");
+
+ subject = requester.getSigner();
+
+ /*
+ * Encode cert request info, wrap in a sequence for signing
+ */
+ scratch = new DerOutputStream();
+ scratch.putInteger(new BigInt(0)); // version zero
+ subject.encode(scratch); // X.500 name
+ subjectPublicKeyInfo.encode(scratch); // public key
+ attributeSet.encode(scratch);
+
+ out = new DerOutputStream();
+ out.write(DerValue.tag_Sequence, scratch); // wrap it!
+ certificateRequestInfo = out.toByteArray();
+ scratch = out;
+
+ /*
+ * Sign it ...
+ */
+ requester.update(certificateRequestInfo, 0,
+ certificateRequestInfo.length);
+ sig = requester.sign();
+
+ /*
+ * Build guts of SIGNED macro
+ */
+ requester.getAlgorithmId().encode(scratch); // sig algorithm
+ scratch.putBitString(sig); // sig
+
+ /*
+ * Wrap those guts in a sequence
+ */
+ out = new DerOutputStream();
+ out.write(DerValue.tag_Sequence, scratch);
+ certificateRequest = out.toByteArray();
+ }
+
+ /**
+ * Returns the subject's name.
+ */
+ public X500Name getSubjectName() {
+ return subject;
+ }
+
+ /**
+ * Returns the subject's public key.
+ */
+ public X509Key getSubjectPublicKeyInfo() {
+ return subjectPublicKeyInfo;
+ }
+
+ /**
+ * Returns the additional attributes requested.
+ */
+ public PKCS10Attributes getAttributes() {
+ return attributeSet;
+ }
+
+ /**
+ * Returns the encoded and signed certificate request as a
+ * DER-encoded byte array.
+ *
+ * @return the certificate request, or null if encodeAndSign()
+ * has not yet been called.
+ */
+ public byte[] toByteArray() {
+ return certificateRequest;
+ }
+
+ /**
+ * Prints an E-Mailable version of the certificate request on the print
+ * stream passed. The format is a common base64 encoded one, supported
+ * by most Certificate Authorities because Netscape web servers have
+ * used this for some time. Some certificate authorities expect some
+ * more information, in particular contact information for the web
+ * server administrator.
+ *
+ * @param out the print stream where the certificate request
+ * will be printed.
+ * @exception IOException when an output operation failed
+ * @exception SignatureException when the certificate request was
+ * not yet signed.
+ */
+ public void print(PrintStream out)
+ throws IOException, SignatureException {
+ if (certificateRequest == null)
+ throw new SignatureException("Cert request was not signed");
+
+ out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
+ out.println(Utils.base64encode(certificateRequest));
+ out.println("-----END NEW CERTIFICATE REQUEST-----");
+ }
+
+ /**
+ * Provides a short description of this request.
+ */
+ public String toString() {
+ return "[PKCS #10 certificate request:\n"
+ + subjectPublicKeyInfo.toString()
+ + " subject: <" + subject + ">" + "\n"
+ + " attributes: " + attributeSet.toString()
+ + "\n]";
+ }
+
+ /**
+ * Retrieve the PKCS10 CertificateRequestInfo as a byte array
+ */
+ public byte[] getCertRequestInfo() {
+ return certRequestInfo;
+ }
+
+ private X500Name subject;
+ private X509Key subjectPublicKeyInfo;
+ private PKCS10Attributes attributeSet;
+
+ private byte certificateRequest[]; // signed
+ private byte certRequestInfo[]; // inner content signed
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS10Attribute.java b/base/util/src/netscape/security/pkcs/PKCS10Attribute.java
new file mode 100644
index 000000000..a649c395a
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS10Attribute.java
@@ -0,0 +1,238 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.cert.CertificateException;
+
+import netscape.security.util.DerEncoder;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+import netscape.security.x509.ACertAttrSet;
+import netscape.security.x509.CertAttrSet;
+import netscape.security.x509.Extensions;
+import netscape.security.x509.OIDMap;
+
+/**
+ * Represent a PKCS Attribute.
+ *
+ * <p>
+ * Attributes are addiitonal attributes which can be inserted in a PKCS certificate request. For example a
+ * "Driving License Certificate" could have the driving license number as a attribute.
+ *
+ * <p>
+ * Attributes are represented as a sequence of the attribute identifier (Object Identifier) and a set of DER encoded
+ * attribute values. The current implementation only supports one value per attribute.
+ *
+ * ASN.1 definition of Attribute:
+ *
+ * <pre>
+ * Attribute :: SEQUENCE {
+ * type AttributeValue,
+ * values SET OF AttributeValue
+ * }
+ * AttributeValue ::= ANY
+ * </pre>
+ *
+ * @author Amit Kapoor
+ * @author Hemma Prafullchandra
+ * @version 1.13
+ */
+public class PKCS10Attribute implements DerEncoder, Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2002480042340316170L;
+ protected ObjectIdentifier attributeId = null;
+ protected CertAttrSet attributeValue = null;
+
+ /**
+ * Default constructor. Used only by sub-classes.
+ */
+ public PKCS10Attribute() {
+ }
+
+ /**
+ * Constructs an attribute from a DER encoded array of bytes.
+ */
+ public PKCS10Attribute(DerValue derVal) throws IOException {
+ if (derVal.tag != DerValue.tag_Sequence) {
+ throw new IOException("Sequence tag missing for PKCS10Attribute.");
+ }
+
+ DerInputStream in = derVal.toDerInputStream();
+ // Object identifier
+ attributeId = in.getOID();
+ // System.out.println("attribute ID in pkcs10 "+attributeId.toString());
+
+ // Rest of the stuff is attribute value(s), wrapped in a SET.
+ // For now, assume there is only one attribute value present.
+ DerValue[] inAttrValues = in.getSet(1);
+ int attrValueNum = inAttrValues.length;
+ if (attrValueNum > 1) {
+ throw new IOException("More than one value per attribute not supported");
+ }
+
+ // Read the first attribute value
+ DerValue inAttrValue = inAttrValues[0];
+
+ if (attributeId.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
+ //pkcs9 extensionAttr
+ try {
+ // remove the tag
+ //DerValue dv = inAttrValue.data.getDerValue();
+ // hack. toDerInputStream only gives one extension.
+ DerInputStream fi = new DerInputStream(inAttrValue.toByteArray());
+ attributeValue = (CertAttrSet) new
+ Extensions(fi);
+ //CertificateExtensions(fi);
+ return;
+ } catch (Exception e) {
+ throw new IOException(e.toString());
+ }
+ }
+ byte[] val = inAttrValue.toByteArray();
+ Class<?>[] params = { Object.class };
+ try {
+ @SuppressWarnings("unchecked")
+ Class<CertAttrSet> extClass = (Class<CertAttrSet>) OIDMap.getClass(attributeId);
+ if (extClass != null) {
+ Constructor<CertAttrSet> cons = (Constructor<CertAttrSet>) extClass.getConstructor(params);
+ Object value = Array.newInstance(byte.class, val.length);
+ for (int i = 0; i < val.length; i++) {
+ Array.setByte(value, i, val[i]);
+ }
+ Object[] passed = new Object[] { value };
+ attributeValue = cons.newInstance(passed);
+ } else {
+ // attribute classes are usable for PKCS10 attributes.
+ // this is used where the attributes are not actual
+ // implemented extensions.
+ attributeValue = new ACertAttrSet(inAttrValue);
+ }
+ } catch (InvocationTargetException invk) {
+ throw new IOException(invk.getTargetException().getMessage());
+ } catch (Exception e) {
+ throw new IOException(e.toString());
+ }
+ }
+
+ /**
+ * Constructs an attribute from individual components of ObjectIdentifier
+ * and the DER encoded value.
+ *
+ * @param attributeId the ObjectIdentifier of the attribute.
+ * @param attributeValue the CertAttrSet.
+ */
+ public PKCS10Attribute(ObjectIdentifier attributeId,
+ CertAttrSet attributeValue) {
+ this.attributeId = attributeId;
+ this.attributeValue = attributeValue;
+ }
+
+ /**
+ * Constructs an attribute from another attribute. To be used for
+ * creating decoded subclasses.
+ *
+ * @param attr the attribute to create from.
+ */
+ public PKCS10Attribute(PKCS10Attribute attr) {
+ this.attributeId = attr.attributeId;
+ this.attributeValue = attr.attributeValue;
+ }
+
+ /**
+ * Write the output to the DerOutputStream.
+ *
+ * @param out the OutputStream to write the attribute to.
+ * @exception CertificateException on certificate encoding errors.
+ * @exception IOException on encoding errors.
+ */
+ public void encode(OutputStream out)
+ throws CertificateException, IOException {
+ // Encode the attribute value
+ DerOutputStream outAttrValue = new DerOutputStream();
+ attributeValue.encode(outAttrValue);
+
+ // Wrap the encoded attribute value into a SET
+ DerValue outAttrValueSet = new DerValue(DerValue.tag_Set,
+ outAttrValue.toByteArray());
+
+ // Create the attribute
+ DerOutputStream outAttr = new DerOutputStream();
+ outAttr.putOID(attributeId);
+ outAttr.putDerValue(outAttrValueSet);
+
+ // Wrap the OID and the set of attribute values into a SEQUENCE
+ DerOutputStream tmp = new DerOutputStream();
+ tmp.write(DerValue.tag_Sequence, outAttr);
+
+ // write the results to out
+ out.write(tmp.toByteArray());
+ }
+
+ /**
+ * DER encode this object onto an output stream.
+ * Implements the <code>DerEncoder</code> interface.
+ *
+ * @param out
+ * the OutputStream on which to write the DER encoding.
+ *
+ * @exception IOException on encoding errors.
+ */
+ public void derEncode(OutputStream out) throws IOException {
+ try {
+ encode(out);
+ } catch (CertificateException ce) {
+ IOException ioe = new IOException(ce.toString());
+ ioe.fillInStackTrace();
+ throw ioe;
+ }
+ }
+
+ /**
+ * Returns the ObjectIdentifier of the attribute.
+ */
+ public ObjectIdentifier getAttributeId() {
+ return (attributeId);
+ }
+
+ /**
+ * Returns the attribute value as an byte array for further processing.
+ */
+ public CertAttrSet getAttributeValue() {
+ return (attributeValue);
+ }
+
+ /**
+ * Returns the attribute in user readable form.
+ */
+ public String toString() {
+ String s = "AttributeId: " + attributeId.toString() + "\n";
+ s += "AttributeValue: " + attributeValue.toString();
+
+ return (s);
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS10Attributes.java b/base/util/src/netscape/security/pkcs/PKCS10Attributes.java
new file mode 100644
index 000000000..8beb1e64c
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS10Attributes.java
@@ -0,0 +1,147 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import netscape.security.util.DerEncoder;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+
+/**
+ * This class defines the PKCS10 attributes for the request.
+ *
+ * @author Amit Kapoor
+ * @author Hemma Prafullchandra
+ * @version 1.10
+ */
+public class PKCS10Attributes extends Vector<PKCS10Attribute> implements DerEncoder {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1362260612357629542L;
+ private Hashtable<String, PKCS10Attribute> map;
+
+ /**
+ * Default constructor for the certificate attribute.
+ */
+ public PKCS10Attributes() {
+ map = new Hashtable<String, PKCS10Attribute>();
+ }
+
+ /**
+ * Create the object, decoding the values from the passed DER stream.
+ *
+ * @param in the DerInputStream to read the attributes from.
+ * @exception IOException on decoding errors.
+ */
+ public PKCS10Attributes(DerInputStream in)
+ throws IOException {
+
+ map = new Hashtable<String, PKCS10Attribute>();
+ DerValue[] attrs = in.getSet(5, true);
+
+ if (attrs != null) {
+ for (int i = 0; i < attrs.length; i++) {
+ PKCS10Attribute attr = new PKCS10Attribute(attrs[i]);
+ addElement(attr);
+ map.put(attr.getAttributeValue().getName(), attr);
+ }
+ }
+ }
+
+ /**
+ * Encode the attributes in DER form to the stream.
+ *
+ * @param out the OutputStream to marshal the contents to.
+ *
+ * @exception IOException on encoding errors.
+ */
+ public void encode(OutputStream out)
+ throws IOException {
+ derEncode(out);
+ }
+
+ /**
+ * Encode the attributes in DER form to the stream.
+ * Implements the <code>DerEncoder</code> interface.
+ *
+ * @param out the OutputStream to marshal the contents to.
+ * @exception IOException on encoding errors.
+ */
+ public void derEncode(OutputStream out)
+ throws IOException {
+
+ // first copy the elements into an array
+ PKCS10Attribute[] attribs = new PKCS10Attribute[size()];
+ copyInto(attribs);
+
+ DerOutputStream attrOut = new DerOutputStream();
+ attrOut.putOrderedSetOf(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0),
+ attribs);
+
+ out.write(attrOut.toByteArray());
+ }
+
+ /**
+ * Set the attribute value.
+ */
+ public void setAttribute(String name, PKCS10Attribute attr) throws IOException {
+ map.put(name, attr);
+ addElement(attr);
+ }
+
+ /**
+ * Get the attribute value.
+ */
+ public PKCS10Attribute getAttribute(String name) throws IOException {
+ PKCS10Attribute attr = map.get(name);
+ /*
+ if (attr == null) {
+ throw new IOException("No attribute found with name " + name);
+ }
+ */
+ return (attr);
+ }
+
+ /**
+ * Delete the attribute value.
+ */
+ public void deleteAttribute(String name) throws IOException {
+ PKCS10Attribute attr = map.get(name);
+ if (attr == null) {
+ throw new IOException("No attribute found with name " + name);
+ }
+ map.remove(name);
+ removeElement(attr);
+ }
+
+ /**
+ * Return an enumeration of names of attributes existing within this
+ * attribute.
+ */
+ public Enumeration<PKCS10Attribute> getElements() {
+ return map.elements();
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS7.java b/base/util/src/netscape/security/pkcs/PKCS7.java
new file mode 100644
index 000000000..c8fb69582
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS7.java
@@ -0,0 +1,446 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Vector;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertImpl;
+
+/**
+ * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
+ * Supports only <tt>SignedData</tt> ContentInfo
+ * type, where to the type of data signed is plain Data.
+ * For signedData, <tt>crls</tt>, <tt>attributes</tt> and
+ * PKCS#6 Extended Certificates are not supported.
+ *
+ * @version 1.33 97/12/10
+ * @author Benjamin Renaud
+ */
+public class PKCS7 {
+
+ private ObjectIdentifier contentType;
+
+ // the ASN.1 members for a signedData (and other) contentTypes
+ private BigInt version;
+ private AlgorithmId[] digestAlgorithmIds;
+ private ContentInfo contentInfo;
+ private X509Certificate[] certificates;
+ private SignerInfo[] signerInfos;
+
+ /**
+ * Unmarshals a PKCS7 block from its encoded form, parsing the
+ * encoded bytes from the InputStream.
+ *
+ * @param in an input stream holding at least one PKCS7 block.
+ * @exception ParsingException on parsing errors.
+ * @exception IOException on other errors.
+ */
+ public PKCS7(InputStream in) throws ParsingException, IOException {
+ DataInputStream dis = new DataInputStream(in);
+
+ int len = 0;
+ byte[] newbuf = new byte[len];
+ byte[] oldbuf = new byte[len];
+ byte[] data = new byte[len];
+
+ do {
+ newbuf = new byte[dis.available()];
+ len += dis.available();
+ dis.readFully(newbuf);
+ data = new byte[len];
+
+ System.arraycopy(oldbuf, 0, data, 0, oldbuf.length);
+ System.arraycopy(newbuf, 0, data, oldbuf.length, newbuf.length);
+ oldbuf = new byte[len];
+ System.arraycopy(data, 0, oldbuf, 0, data.length);
+
+ } while (dis.available() > 0);
+
+ parse(new DerInputStream(data));
+ }
+
+ /**
+ * Unmarshals a PKCS7 block from its encoded form, parsing the
+ * encoded bytes from the DerInputStream.
+ *
+ * @param derin a DerInputStream holding at least one PKCS7 block.
+ * @exception ParsingException on parsing errors.
+ */
+ public PKCS7(DerInputStream derin) throws ParsingException {
+ parse(derin);
+ }
+
+ /**
+ * Unmarshals a PKCS7 block from its encoded form, parsing the
+ * encoded bytes.
+ *
+ * @param bytes the encoded bytes.
+ * @exception ParsingException on parsing errors.
+ */
+ public PKCS7(byte[] bytes) throws ParsingException {
+ DerInputStream derin = new DerInputStream(bytes);
+ parse(derin);
+ }
+
+ private void parse(DerInputStream derin) throws ParsingException {
+ try {
+ ContentInfo contentInfo = new ContentInfo(derin);
+ contentType = contentInfo.contentType;
+ if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) {
+ parseSignedData(contentInfo.getContent());
+ } else {
+ throw new ParsingException("content type " + contentType +
+ " not supported.");
+ }
+ } catch (IOException e) {
+ ParsingException pe =
+ new ParsingException("IOException: " + e.getMessage());
+ pe.fillInStackTrace();
+ throw pe;
+ }
+ }
+
+ /**
+ * Construct an initialized PKCS7 block.
+ *
+ * @param digestAlgorithmIds the message digest algorithm identifiers.
+ * @param contentInfo the content information.
+ * @param certificates an array of X.509 certificates.
+ * @param signerInfos an array of signer information.
+ */
+ public PKCS7(AlgorithmId[] digestAlgorithmIds,
+ ContentInfo contentInfo,
+ X509Certificate[] certificates,
+ SignerInfo[] signerInfos) {
+
+ version = new BigInt(1);
+ this.digestAlgorithmIds = digestAlgorithmIds;
+ this.contentInfo = contentInfo;
+ this.certificates = certificates;
+ this.signerInfos = signerInfos;
+ }
+
+ private void parseSignedData(DerValue val)
+ throws ParsingException, IOException {
+
+ DerInputStream dis = val.toDerInputStream();
+
+ // Version
+ version = dis.getInteger();
+
+ // digestAlgorithmIds
+ DerValue[] digestAlgorithmIdVals = dis.getSet(1);
+ int len = digestAlgorithmIdVals.length;
+ digestAlgorithmIds = new AlgorithmId[len];
+ try {
+ for (int i = 0; i < len; i++) {
+ DerValue oid = digestAlgorithmIdVals[i];
+ digestAlgorithmIds[i] = AlgorithmId.parse(oid);
+ }
+
+ } catch (IOException e) {
+ ParsingException pe =
+ new ParsingException("Error parsing digest AlgorithmId IDs: " +
+ e.getMessage());
+ pe.fillInStackTrace();
+ throw pe;
+ }
+ // contentInfo
+ contentInfo = new ContentInfo(dis);
+
+ /*
+ * check if certificates (implicit tag) are provided
+ * (certificates are OPTIONAL)
+ */
+ if ((byte) (dis.peekByte()) == (byte) 0xA0) {
+ DerValue[] certificateVals = dis.getSet(2, true);
+
+ len = certificateVals.length;
+ certificates = new X509Certificate[len];
+
+ for (int i = 0; i < len; i++) {
+ try {
+ X509Certificate cert = (X509Certificate) new
+ X509CertImpl(certificateVals[i]);
+ certificates[i] = cert;
+ } catch (CertificateException e) {
+ ParsingException pe =
+ new ParsingException("CertificateException: " +
+ e.getMessage());
+ pe.fillInStackTrace();
+ throw pe;
+ }
+ }
+ }
+
+ // check if crls (implicit tag) are provided (crls are OPTIONAL)
+ if ((byte) (dis.peekByte()) == (byte) 0xA1) {
+ dis.getSet(0, true);
+ }
+
+ // signerInfos
+ DerValue[] signerInfoVals = dis.getSet(1);
+
+ len = signerInfoVals.length;
+ signerInfos = new SignerInfo[len];
+
+ for (int i = 0; i < len; i++) {
+ DerInputStream in = signerInfoVals[i].toDerInputStream();
+ signerInfos[i] = new SignerInfo(in);
+ }
+
+ }
+
+ /**
+ * Encodes the signed data to an output stream.
+ *
+ * @param out the output stream to write the encoded data to.
+ * @exception IOException on encoding errors.
+ */
+ public void encodeSignedData(OutputStream out) throws IOException {
+ DerOutputStream derout = new DerOutputStream();
+ encodeSignedData(derout, true);
+ out.write(derout.toByteArray());
+ }
+
+ /**
+ * Like method above but not sorted.
+ */
+ public void encodeSignedData(OutputStream out, boolean sort)
+ throws IOException {
+ DerOutputStream derout = new DerOutputStream();
+ encodeSignedData(derout, sort);
+ out.write(derout.toByteArray());
+ }
+
+ /**
+ * encode signed data, sort certs by default.
+ */
+ public void encodeSignedData(DerOutputStream out)
+ throws IOException {
+ encodeSignedData(out, true);
+ }
+
+ /**
+ * Encodes the signed data to a DerOutputStream.
+ *
+ * @param out the DerOutputStream to write the encoded data to.
+ * @exception IOException on encoding errors.
+ */
+ public void encodeSignedData(DerOutputStream out, boolean sort)
+ throws IOException {
+
+ DerOutputStream signedData = new DerOutputStream();
+
+ // version
+ signedData.putInteger(version);
+
+ // digestAlgorithmIds
+ signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
+
+ // contentInfo
+ contentInfo.encode(signedData);
+
+ // cast to X509CertImpl[] since X509CertImpl implements DerEncoder
+ X509CertImpl implCerts[] = new X509CertImpl[certificates.length];
+ try {
+ for (int i = 0; i < certificates.length; i++) {
+ implCerts[i] = (X509CertImpl) certificates[i];
+ }
+ } catch (ClassCastException e) {
+ IOException ioe =
+ new IOException("Certificates in PKCS7 " +
+ "must be of class " +
+ "netscape.security.X509CertImpl");
+ ioe.fillInStackTrace();
+ }
+
+ // Add the certificate set (tagged with [0] IMPLICIT)
+ // to the signed data
+ if (sort) {
+ signedData.putOrderedSetOf((byte) 0xA0, implCerts);
+ } else {
+ signedData.putSet((byte) 0xA0, implCerts);
+ }
+
+ // no crls (OPTIONAL field)
+
+ // signerInfos
+ signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
+
+ // making it a signed data block
+ DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
+ signedData.toByteArray());
+
+ // making it a content info sequence
+ ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,
+ signedDataSeq);
+
+ // writing out the contentInfo sequence
+ block.encode(out);
+ }
+
+ /**
+ * This verifies a given SignerInfo.
+ *
+ * @param info the signer information.
+ * @param bytes the DER encoded content information.
+ *
+ * @exception NoSuchAlgorithmException on unrecognized algorithms.
+ * @exception SignatureException on signature handling errors.
+ */
+ public SignerInfo verify(SignerInfo info, byte[] bytes)
+ throws NoSuchAlgorithmException, SignatureException {
+ return info.verify(this, bytes);
+ }
+
+ /**
+ * Returns all signerInfos which self-verify.
+ *
+ * @param bytes the DER encoded content information.
+ *
+ * @exception NoSuchAlgorithmException on unrecognized algorithms.
+ * @exception SignatureException on signature handling errors.
+ */
+ public SignerInfo[] verify(byte[] bytes)
+ throws NoSuchAlgorithmException, SignatureException {
+
+ Vector<SignerInfo> intResult = new Vector<SignerInfo>();
+ for (int i = 0; i < signerInfos.length; i++) {
+
+ SignerInfo signerInfo = verify(signerInfos[i], bytes);
+ if (signerInfo != null) {
+ intResult.addElement(signerInfo);
+ }
+ }
+ if (intResult.size() != 0) {
+
+ SignerInfo[] result = new SignerInfo[intResult.size()];
+ intResult.copyInto(result);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * Returns all signerInfos which self-verify.
+ *
+ * @exception NoSuchAlgorithmException on unrecognized algorithms.
+ * @exception SignatureException on signature handling errors.
+ */
+ public SignerInfo[] verify()
+ throws NoSuchAlgorithmException, SignatureException {
+ return verify(null);
+ }
+
+ /**
+ * Returns the version number of this PKCS7 block.
+ */
+ public BigInt getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the message digest algorithms specified in this PKCS7 block.
+ */
+ public AlgorithmId[] getDigestAlgorithmIds() {
+ return digestAlgorithmIds;
+ }
+
+ /**
+ * Returns the content information specified in this PKCS7 block.
+ */
+ public ContentInfo getContentInfo() {
+ return contentInfo;
+ }
+
+ /**
+ * Returns the X.509 certificates listed in this PKCS7 block.
+ */
+ public X509Certificate[] getCertificates() {
+ return certificates;
+ }
+
+ /**
+ * Returns the signer's information specified in this PKCS7 block.
+ */
+ public SignerInfo[] getSignerInfos() {
+ return signerInfos;
+ }
+
+ /**
+ * Returns the X.509 certificate listed in this PKCS7 block
+ * which has a matching serial number and Issuer name, or
+ * null if one is not found.
+ *
+ * @param serial the serial number of the certificate to retrieve.
+ * @param name the Distinguished Name of the Issuer.
+ */
+ public X509Certificate getCertificate(BigInt serial, X500Name name) {
+
+ for (int i = 0; i < certificates.length; i++) {
+ X509Certificate cert = certificates[i];
+ X500Name thisName = (X500Name) cert.getIssuerDN();
+ BigInteger tmpSerial = (BigInteger) cert.getSerialNumber();
+ BigInt thisSerial = new BigInt(tmpSerial);
+ if (serial.equals(thisSerial) && name.equals(thisName)) {
+ return cert;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the PKCS7 block in a printable string form.
+ */
+ public String toString() {
+ String out = "";
+
+ out += "PKCS7 :: version: " + version + "\n";
+ out += "PKCS7 :: digest AlgorithmIds: \n";
+ for (int i = 0; i < digestAlgorithmIds.length; i++) {
+ out += "\t" + digestAlgorithmIds[i] + "\n";
+ }
+ out += contentInfo + "\n";
+ out += "PKCS7 :: certificates: \n";
+ for (int i = 0; i < certificates.length; i++) {
+ out += "\t" + i + ". " + certificates[i] + "\n";
+ }
+ out += "PKCS7 :: signer infos: \n";
+ for (int i = 0; i < signerInfos.length; i++) {
+ out += ("\t" + i + ". " + signerInfos[i] + "\n");
+ }
+ return out;
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS8Key.java b/base/util/src/netscape/security/pkcs/PKCS8Key.java
new file mode 100644
index 000000000..7c5f64582
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS8Key.java
@@ -0,0 +1,435 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.x509.AlgorithmId;
+
+/**
+ * Holds a PKCS#8 key, for example a private key
+ *
+ * @version 1.30, 97/12/10
+ * @author Dave Brownell
+ * @author Benjamin Renaud
+ */
+public class PKCS8Key implements PrivateKey {
+
+ /** use serialVersionUID from JDK 1.1. for interoperability */
+ private static final long serialVersionUID = -3836890099307167124L;
+
+ /* The algorithm information (name, parameters, etc). */
+ protected AlgorithmId algid;
+
+ /* The key bytes, without the algorithm information */
+ protected byte[] key;
+
+ /* The encoded for the key. */
+ protected byte[] encodedKey;
+
+ /* The version for this key */
+ public static final BigInteger VERSION = BigInteger.valueOf(0);
+
+ /**
+ * Default constructor. The key constructed must have its key
+ * and algorithm initialized before it may be used, for example
+ * by using <code>decode</code>.
+ */
+ public PKCS8Key() {
+ }
+
+ /**
+ * Construct PKCS#8 subject public key from a DER value. If
+ * the runtime environment is configured with a specific class for
+ * this kind of key, a subclass is returned. Otherwise, a generic
+ * PKCS8Key object is returned.
+ *
+ * <P>
+ * This mechanism gurantees that keys (and algorithms) may be freely manipulated and transferred, without risk of
+ * losing information. Also, when a key (or algorithm) needs some special handling, that specific need can be
+ * accomodated.
+ *
+ * @param in the DER-encoded SubjectPublicKeyInfo value
+ * @exception IOException on data format errors
+ */
+ public static PKCS8Key parse(DerValue in) throws IOException {
+ AlgorithmId algorithm;
+ PKCS8Key subjectKey;
+
+ if (in.tag != DerValue.tag_Sequence)
+ throw new IOException("corrupt private key");
+
+ BigInteger parsedVersion = in.data.getInteger().toBigInteger();
+ if (!VERSION.equals(parsedVersion)) {
+ throw new IOException("version mismatch: (supported: " +
+ VERSION + ", parsed: " +
+ parsedVersion);
+ }
+
+ algorithm = AlgorithmId.parse(in.data.getDerValue());
+
+ try {
+ subjectKey = buildPKCS8Key(algorithm, in.data.getOctetString());
+
+ } catch (InvalidKeyException e) {
+ throw new IOException("corrupt private key");
+ }
+
+ if (in.data.available() != 0)
+ throw new IOException("excess private key");
+ return subjectKey;
+ }
+
+ /**
+ * Parse the key bits. This may be redefined by subclasses to take
+ * advantage of structure within the key. For example, RSA public
+ * keys encapsulate two unsigned integers (modulus and exponent) as
+ * DER values within the <code>key</code> bits; Diffie-Hellman and
+ * DSS/DSA keys encapsulate a single unsigned integer.
+ *
+ * <P>
+ * This function is called when creating PKCS#8 SubjectPublicKeyInfo values using the PKCS8Key member functions,
+ * such as <code>parse</code> and <code>decode</code>.
+ *
+ * @exception IOException if a parsing error occurs.
+ * @exception InvalidKeyException if the key encoding is invalid.
+ */
+ protected void parseKeyBits() throws IOException, InvalidKeyException {
+ encode();
+ }
+
+ /*
+ * Factory interface, building the kind of key associated with this
+ * specific algorithm ID or else returning this generic base class.
+ * See the description above.
+ */
+ public static PKCS8Key buildPKCS8Key(AlgorithmId algid, byte[] key)
+ throws IOException, InvalidKeyException {
+ /*
+ * Use the algid and key parameters to produce the ASN.1 encoding
+ * of the key, which will then be used as the input to the
+ * key factory.
+ */
+ DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream();
+ encode(pkcs8EncodedKeyStream, algid, key);
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray());
+
+ try {
+ // Instantiate the key factory of the appropriate algorithm
+ KeyFactory keyFac = KeyFactory.getInstance(algid.getName());
+
+ // Generate the private key
+ PrivateKey privKey = keyFac.generatePrivate(pkcs8KeySpec);
+
+ if (privKey instanceof PKCS8Key) {
+ /*
+ * Return specialized PKCS8Key, where the structure within the
+ * key has been parsed
+ */
+ return (PKCS8Key) privKey;
+ }
+ } catch (NoSuchAlgorithmException e) {
+ // Return generic PKCS8Key with opaque key data (see below)
+ } catch (InvalidKeySpecException e) {
+ // Return generic PKCS8Key with opaque key data (see below)
+ }
+
+ /*
+ * Try again using JDK1.1-style for backwards compatibility.
+ */
+ String classname = "";
+ try {
+ Provider sunProvider;
+
+ sunProvider = Security.getProvider("SUN");
+ if (sunProvider == null)
+ throw new InstantiationException();
+ classname = sunProvider.getProperty("PrivateKey.PKCS#8." +
+ algid.getName());
+ if (classname == null) {
+ throw new InstantiationException();
+ }
+
+ Class<?> keyClass = Class.forName(classname);
+ Object inst;
+ PKCS8Key result;
+
+ inst = keyClass.newInstance();
+ if (inst instanceof PKCS8Key) {
+ result = (PKCS8Key) inst;
+ result.algid = algid;
+ result.key = key;
+ result.parseKeyBits();
+ return result;
+ }
+ } catch (ClassNotFoundException e) {
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ // this should not happen.
+ throw new IOException(classname + " [internal error]");
+ }
+
+ PKCS8Key result = new PKCS8Key();
+ result.algid = algid;
+ result.key = key;
+ return result;
+ }
+
+ /**
+ * Returns the algorithm to be used with this key.
+ */
+ public String getAlgorithm() {
+ return algid.getName();
+ }
+
+ /**
+ * Returns the algorithm ID to be used with this key.
+ */
+ public AlgorithmId getAlgorithmId() {
+ return algid;
+ }
+
+ /**
+ * PKCS#8 sequence on the DER output stream.
+ */
+ public final void encode(DerOutputStream out) throws IOException {
+ encode(out, this.algid, this.key);
+ }
+
+ /**
+ * Returns the DER-encoded form of the key as a byte array.
+ */
+ public synchronized byte[] getEncoded() {
+ byte[] result = null;
+ try {
+ result = encode();
+ } catch (InvalidKeyException e) {
+ }
+ return result;
+ }
+
+ /**
+ * Returns the format for this key: "PKCS#8"
+ */
+ public String getFormat() {
+ return "PKCS#8";
+ }
+
+ /**
+ * Returns the DER-encoded form of the key as a byte array.
+ *
+ * @exception InvalidKeyException if an encoding error occurs.
+ */
+ public byte[] encode() throws InvalidKeyException {
+ if (encodedKey == null) {
+ try {
+ DerOutputStream out;
+
+ out = new DerOutputStream();
+ encode(out);
+ encodedKey = out.toByteArray();
+
+ } catch (IOException e) {
+ throw new InvalidKeyException("IOException : " +
+ e.getMessage());
+ }
+ }
+ return copyEncodedKey(encodedKey);
+ }
+
+ /*
+ * Returns a printable representation of the key
+ */
+ public String toString() {
+ netscape.security.util.PrettyPrintFormat pp =
+ new netscape.security.util.PrettyPrintFormat(" ", 20);
+ String keybits = pp.toHexString(key);
+
+ return "algorithm = " + algid.toString()
+ + ", unparsed keybits = \n" + keybits;
+ }
+
+ /**
+ * Initialize an PKCS8Key object from an input stream. The data
+ * on that input stream must be encoded using DER, obeying the
+ * PKCS#8 format: a sequence consisting of a version, an algorithm
+ * ID and a bit string which holds the key. (That bit string is
+ * often used to encapsulate another DER encoded sequence.)
+ *
+ * <P>
+ * Subclasses should not normally redefine this method; they should instead provide a <code>parseKeyBits</code>
+ * method to parse any fields inside the <code>key</code> member.
+ *
+ * @param in an input stream with a DER-encoded PKCS#8
+ * SubjectPublicKeyInfo value
+ *
+ * @exception InvalidKeyException if a parsing error occurs.
+ */
+ public void decode(InputStream in) throws InvalidKeyException {
+ DerValue val;
+
+ try {
+ val = new DerValue(in);
+ if (val.tag != DerValue.tag_Sequence)
+ throw new InvalidKeyException("invalid key format");
+
+ BigInteger version = val.data.getInteger().toBigInteger();
+ if (!version.equals(PKCS8Key.VERSION)) {
+ throw new IOException("version mismatch: (supported: " +
+ PKCS8Key.VERSION + ", parsed: " +
+ version);
+ }
+ algid = AlgorithmId.parse(val.data.getDerValue());
+ key = val.data.getOctetString();
+ parseKeyBits();
+ if (val.data.available() != 0)
+ throw new InvalidKeyException("excess key data");
+
+ } catch (IOException e) {
+ // e.printStackTrace ();
+ throw new InvalidKeyException("IOException : " +
+ e.getMessage());
+ }
+ }
+
+ public void decode(byte[] encodedKey) throws InvalidKeyException {
+ decode(new ByteArrayInputStream(encodedKey));
+ }
+
+ /**
+ * Serialization write ... PKCS#8 keys serialize as
+ * themselves, and they're parsed when they get read back.
+ */
+ private synchronized void
+ writeObject(java.io.ObjectOutputStream stream)
+ throws IOException {
+ stream.write(getEncoded());
+ }
+
+ /**
+ * Serialization read ... PKCS#8 keys serialize as
+ * themselves, and they're parsed when they get read back.
+ */
+ private synchronized void readObject(ObjectInputStream stream)
+ throws IOException {
+
+ try {
+ decode(stream);
+
+ } catch (InvalidKeyException e) {
+ e.printStackTrace();
+ throw new IOException("deserialized key is invalid: " +
+ e.getMessage());
+ }
+ }
+
+ /*
+ * Make a copy of the encoded key.
+ */
+ private byte[] copyEncodedKey(byte[] encodedKey) {
+ int len = encodedKey.length;
+ byte[] copy = new byte[len];
+ System.arraycopy(encodedKey, 0, copy, 0, len);
+ return copy;
+ }
+
+ /*
+ * Produce PKCS#8 encoding from algorithm id and key material.
+ */
+ static void encode(DerOutputStream out, AlgorithmId algid, byte[] key)
+ throws IOException {
+ DerOutputStream tmp = new DerOutputStream();
+ tmp.putInteger(new BigInt(VERSION.toByteArray()));
+ algid.encode(tmp);
+ tmp.putOctetString(key);
+ out.write(DerValue.tag_Sequence, tmp);
+ }
+
+ /**
+ * Compares two private keys. This returns false if the object with which
+ * to compare is not of type <code>Key</code>.
+ * Otherwise, the encoding of this key object is compared with the
+ * encoding of the given key object.
+ *
+ * @param object the object with which to compare
+ * @return <code>true</code> if this key has the same encoding as the
+ * object argument; <code>false</code> otherwise.
+ */
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (object instanceof Key) {
+
+ // this encoding
+ byte[] b1;
+ if (encodedKey != null) {
+ b1 = encodedKey;
+ } else {
+ b1 = getEncoded();
+ }
+
+ // that encoding
+ byte[] b2 = ((Key) object).getEncoded();
+
+ // do the comparison
+ int i;
+ if (b1.length != b2.length)
+ return false;
+ for (i = 0; i < b1.length; i++) {
+ if (b1[i] != b2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculates a hash code value for this object. Objects
+ * which are equal will also have the same hashcode.
+ */
+ public int hashCode() {
+ int retval = 0;
+ byte[] b1 = getEncoded();
+
+ for (int i = 1; i < b1.length; i++) {
+ retval += b1[i] * i;
+ }
+ return (retval);
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS9Attribute.java b/base/util/src/netscape/security/pkcs/PKCS9Attribute.java
new file mode 100644
index 000000000..6a6fd7dc9
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS9Attribute.java
@@ -0,0 +1,1123 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.cert.CertificateException;
+import java.util.Date;
+import java.util.Hashtable;
+
+import netscape.security.util.DerEncoder;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+import netscape.security.x509.CertificateExtensions;
+
+/**
+ * Class supporting any PKCS9 attribute except
+ * ExtendedCertificateAttribute. Supports DER decoding and access to
+ * attribute values, but not DER encoding or setting of values.
+ *
+ * @version 1.2 97/12/10
+ * @author Douglas Hoover
+ */
+public class PKCS9Attribute implements DerEncoder {
+
+ /*
+ * OIDs of PKCS #9 attribute types.
+ */
+ private static final String RSADSI_str = "1.2.840.113549";
+ private static final String PKCS_str = RSADSI_str + ".1";
+ private static final String PKCS9_str = PKCS_str + ".9";
+
+ /**
+ * Array of attribute OIDs defined in PKCS9, by number.
+ */
+ static final ObjectIdentifier[] PKCS9_OIDS =
+ //new ObjectIdentifier[10];
+ // There are some Obsolete(?) attribute identifiers.
+ // This is mainly for extensionRequest (14) in pkcs10.
+ // We just add the other 4 as by products.
+ new ObjectIdentifier[15];
+
+ static { // static initializer for PKCS9_OIDS
+ for (int i = 1; i < PKCS9_OIDS.length; i++) {
+ PKCS9_OIDS[i] = new ObjectIdentifier(PKCS9_str + "." + i);
+ }
+ }
+
+ public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1];
+ public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2];
+ public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3];
+ public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4];
+ public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5];
+ public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6];
+ public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = PKCS9_OIDS[7];
+ public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = PKCS9_OIDS[8];
+ public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID = PKCS9_OIDS[9];
+
+ public static final ObjectIdentifier ISSUER_AND_SERIALNUMBER_OID = PKCS9_OIDS[10];
+ public static final ObjectIdentifier PASSWORD_CHECK_OID = PKCS9_OIDS[11];
+ public static final ObjectIdentifier PUBLIC_KEY_OID = PKCS9_OIDS[12];
+ public static final ObjectIdentifier SIGNING_DESCRIPTION_OID = PKCS9_OIDS[13];
+ public static final ObjectIdentifier EXTENSION_REQUEST_OID = PKCS9_OIDS[14];
+
+ public static final String EMAIL_ADDRESS_STR = "EmailAddress";
+ public static final String UNSTRUCTURED_NAME_STR = "UnstructuredName";
+ public static final String CONTENT_TYPE_STR = "ContentType";
+ public static final String MESSAGE_DIGEST_STR = "MessageDigest";
+ public static final String SIGNING_TIME_STR = "SigningTime";
+ public static final String COUNTERSIGNATURE_STR = "Countersignature";
+ public static final String CHALLENGE_PASSWORD_STR = "ChallengePassword";
+ public static final String UNSTRUCTURED_ADDRESS_STR = "UnstructuredAddress";
+ public static final String EXTENDED_CERTIFICATE_ATTRIBUTES_STR = "ExtendedCertificateAttributes";
+
+ public static final String ISSUER_AND_SERIALNUMBER_STR = "IssuerAndSerialNumber";
+ public static final String PASSWORD_CHECK_STR = "PasswordCheck";
+ public static final String PUBLIC_KEY_STR = "PublicKey";
+ public static final String SIGNING_DESCRIPTION_STR = "SigningDescription";
+ public static final String EXTENSION_REQUEST_STR = "ExtensionRequest";
+
+ /**
+ * Hashtable mapping names and variant names of supported
+ * attributes to their OIDs. This table contains all name forms
+ * that occur in PKCS9, in lower case.
+ */
+ private static final Hashtable<String, ObjectIdentifier> NAME_OID_TABLE = new Hashtable<String, ObjectIdentifier>(
+ 28);
+
+ static { // static initializer for PCKS9_NAMES
+ NAME_OID_TABLE.put("emailaddress", PKCS9_OIDS[1]);
+ NAME_OID_TABLE.put("unstructuredname", PKCS9_OIDS[2]);
+ NAME_OID_TABLE.put("contenttype", PKCS9_OIDS[3]);
+ NAME_OID_TABLE.put("messagedigest", PKCS9_OIDS[4]);
+ NAME_OID_TABLE.put("signingtime", PKCS9_OIDS[5]);
+ NAME_OID_TABLE.put("countersignature", PKCS9_OIDS[6]);
+ NAME_OID_TABLE.put("challengepassword", PKCS9_OIDS[7]);
+ NAME_OID_TABLE.put("unstructuredaddress", PKCS9_OIDS[8]);
+ NAME_OID_TABLE.put("extendedcertificateattributes", PKCS9_OIDS[9]);
+
+ NAME_OID_TABLE.put("issuerandserialNumber", PKCS9_OIDS[10]);
+ NAME_OID_TABLE.put("passwordcheck", PKCS9_OIDS[11]);
+ NAME_OID_TABLE.put("publickey", PKCS9_OIDS[12]);
+ NAME_OID_TABLE.put("signingdescription", PKCS9_OIDS[13]);
+ NAME_OID_TABLE.put("extensionrequest", PKCS9_OIDS[14]);
+ };
+
+ /**
+ * Hashtable mapping attribute OIDs defined in PKCS9 to the
+ * corresponding attribute value type.
+ */
+ private static final Hashtable<ObjectIdentifier, String> OID_NAME_TABLE = new Hashtable<ObjectIdentifier, String>(
+ 14);
+ static {
+ OID_NAME_TABLE.put(PKCS9_OIDS[1], EMAIL_ADDRESS_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[2], UNSTRUCTURED_NAME_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[3], CONTENT_TYPE_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[4], MESSAGE_DIGEST_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[5], SIGNING_TIME_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[6], COUNTERSIGNATURE_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[7], CHALLENGE_PASSWORD_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[8], UNSTRUCTURED_ADDRESS_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[9], EXTENDED_CERTIFICATE_ATTRIBUTES_STR);
+
+ OID_NAME_TABLE.put(PKCS9_OIDS[10], ISSUER_AND_SERIALNUMBER_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[11], PASSWORD_CHECK_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[12], PUBLIC_KEY_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[13], SIGNING_DESCRIPTION_STR);
+ OID_NAME_TABLE.put(PKCS9_OIDS[14], EXTENSION_REQUEST_STR);
+ }
+
+ /**
+ * Acceptable ASN.1 tags for DER encodings of values of PKCS9
+ * attributes, by index in <code>PKCS9_OIDS</code>.
+ * Sets of acceptable tags are represented as arrays.
+ */
+ private static final Byte[][] PKCS9_VALUE_TAGS = {
+ null,
+ { Byte.valueOf(DerValue.tag_IA5String) }, // EMailAddress
+ { Byte.valueOf(DerValue.tag_IA5String) }, // UnstructuredName
+ { Byte.valueOf(DerValue.tag_ObjectId) }, // ContentType
+ { Byte.valueOf(DerValue.tag_OctetString) }, // MessageDigest
+ { Byte.valueOf(DerValue.tag_UtcTime) }, // SigningTime
+ { Byte.valueOf(DerValue.tag_Sequence) }, // Countersignature
+ { Byte.valueOf(DerValue.tag_PrintableString),
+ Byte.valueOf(DerValue.tag_T61String) }, // ChallengePassword
+ { Byte.valueOf(DerValue.tag_PrintableString),
+ Byte.valueOf(DerValue.tag_T61String) }, // UnstructuredAddress
+ { Byte.valueOf(DerValue.tag_SetOf) }, // ExtendedCertificateAttributes
+
+ null, //IssuerAndSerialNumber
+ null, //PasswordCheck
+ null, //PublicKey
+ null, //SigningDescription
+ { Byte.valueOf(DerValue.tag_Sequence) } //ExtensionRequest
+ };
+
+ /**
+ * Class types required for values for a given PKCS9
+ * attribute type.
+ *
+ * <P>
+ * The following table shows the correspondence between attribute types and value component classes.
+ *
+ * <P>
+ * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
+ *
+ * <TR>
+ * <TH>OID</TH>
+ * <TH>Attribute Type Name</TH>
+ * <TH>Kind</TH>
+ * <TH>Value Class</TH>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.1</TD>
+ * <TD>EmailAddress</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.2</TD>
+ * <TD>UnstructuredName</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.3</TD>
+ * <TD>ContentType</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>ObjectIdentifier</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.4</TD>
+ * <TD>MessageDigest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>byte[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.5</TD>
+ * <TD>SigningTime</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Date</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.6</TD>
+ * <TD>Countersignature</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>SignerInfo</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.7</TD>
+ * <TD>ChallengePassword</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.8</TD>
+ * <TD>UnstructuredAddress</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.9</TD>
+ * <TD>ExtendedCertificateAttributes</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.10</TD>
+ * <TD>IssuerAndSerialNumber</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.11</TD>
+ * <TD>PasswordCheck</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.12</TD>
+ * <TD>PublicKey</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.13</TD>
+ * <TD>SigningDescription</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.14</TD>
+ * <TD>ExtensionRequest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Sequence</code></TD>
+ * </TR>
+ *
+ * </TABLE>
+ */
+ private static final Class<?>[] VALUE_CLASSES = new Class[15];
+
+ static {
+ VALUE_CLASSES[0] = null; // not used
+ VALUE_CLASSES[1] = String[].class; // EMailAddress
+ VALUE_CLASSES[2] = String[].class; // UnstructuredName
+ VALUE_CLASSES[3] = ObjectIdentifier.class; // ContentType
+ VALUE_CLASSES[4] = byte[].class; // MessageDigest (byte[])
+ VALUE_CLASSES[5] = Date.class; // SigningTime
+ VALUE_CLASSES[6] = SignerInfo[].class; // Countersignature
+ VALUE_CLASSES[7] = String.class; // ChallengePassword
+ VALUE_CLASSES[8] = String[].class; // UnstructuredAddress
+ VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes
+
+ VALUE_CLASSES[10] = null; // IssuerAndSerialNumber
+ VALUE_CLASSES[11] = null; // PasswordCheck
+ VALUE_CLASSES[12] = null; // PublicKey
+ VALUE_CLASSES[13] = null; // SigningDescription
+ VALUE_CLASSES[14] = CertificateExtensions.class; // ExtensionRequest
+ }
+
+ /**
+ * Array indicating which PKCS9 attributes are single-valued,
+ * by index in <code>PKCS9_OIDS</code>.
+ */
+ private static final boolean[] SINGLE_VALUED =
+ { false,
+ false, // EMailAddress
+ false, // UnstructuredName
+ true, // ContentType
+ true, // MessageDigest
+ true, // SigningTime
+ false, // Countersignature
+ true, // ChallengePassword
+ false, // UnstructuredAddress
+ false, // ExtendedCertificateAttributes
+
+ true, // IssuerAndSerialNumber
+ true, // PasswordCheck
+ true, // PublicKey
+ true, // SigningDescription
+ true // ExtensionRequest
+ };
+
+ /**
+ * The OID of this attribute is <code>PKCS9_OIDS[index]</code>.
+ */
+ private int index;
+
+ /**
+ * Value set of this attribute. Its class is given by <code>VALUE_CLASSES[index]</code>.
+ */
+ private Object value;
+
+ /**
+ * Construct an attribute object from the attribute's OID and
+ * value. If the attribute is single-valued, provide only one
+ * value. If the attribute is
+ * multiple-valued, provide an array containing all the values.
+ * Arrays of length zero are accepted, though probably useless.
+ *
+ * <P>
+ * The following table gives the class that <code>value</code> must have for a given attribute.
+ *
+ * <P>
+ * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
+ *
+ * <TR>
+ * <TH>OID</TH>
+ * <TH>Attribute Type Name</TH>
+ * <TH>Kind</TH>
+ * <TH>Value Class</TH>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.1</TD>
+ * <TD>EmailAddress</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.2</TD>
+ * <TD>UnstructuredName</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.3</TD>
+ * <TD>ContentType</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>ObjectIdentifier</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.4</TD>
+ * <TD>MessageDigest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>byte[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.5</TD>
+ * <TD>SigningTime</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Date</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.6</TD>
+ * <TD>Countersignature</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>SignerInfo[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.7</TD>
+ * <TD>ChallengePassword</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.8</TD>
+ * <TD>UnstructuredAddress</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.9</TD>
+ * <TD>ExtendedCertificateAttributes</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.10</TD>
+ * <TD>IssuerAndSerialNumber</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.11</TD>
+ * <TD>PasswordCheck</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.12</TD>
+ * <TD>PublicKey</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.13</TD>
+ * <TD>SigningDescription</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.14</TD>
+ * <TD>ExtensionRequest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Sequence</code></TD>
+ * </TR>
+ *
+ * </TABLE>
+ */
+ public PKCS9Attribute(ObjectIdentifier oid, Object value)
+ throws IllegalArgumentException {
+
+ init(oid, value);
+ }
+
+ /**
+ * Construct an attribute object from the attribute's name and
+ * value. If the attribute is single-valued, provide only one
+ * value. If the attribute is
+ * multiple-valued, provide an array containing all the values.
+ * Arrays of length zero are accepted, though probably useless.
+ *
+ * <P>
+ * The following table gives the class that <code>value</code> must have for a given attribute. Reasonable variants
+ * of these attributes are accepted; in particular, case does not matter.
+ *
+ * <P>
+ * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
+ *
+ * <TR>
+ * <TH>OID</TH>
+ * <TH>Attribute Type Name</TH>
+ * <TH>Kind</TH>
+ * <TH>Value Class</TH>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.1</TD>
+ * <TD>EmailAddress</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.2</TD>
+ * <TD>UnstructuredName</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.3</TD>
+ * <TD>ContentType</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>ObjectIdentifier</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.4</TD>
+ * <TD>MessageDigest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>byte[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.5</TD>
+ * <TD>SigningTime</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Date</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.6</TD>
+ * <TD>Countersignature</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>SignerInfo[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.7</TD>
+ * <TD>ChallengePassword</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.8</TD>
+ * <TD>UnstructuredAddress</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.9</TD>
+ * <TD>ExtendedCertificateAttributes</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.10</TD>
+ * <TD>IssuerAndSerialNumber</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.11</TD>
+ * <TD>PasswordCheck</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.12</TD>
+ * <TD>PublicKey</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.13</TD>
+ * <TD>SigningDescription</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.14</TD>
+ * <TD>ExtensionRequest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Sequence</code></TD>
+ * </TR>
+ *
+ * </TABLE>
+ *
+ * @exception IllegalArgumentException
+ * if the <code>name</code> is not recognized of the <code>value</code> has the wrong type.
+ */
+ public PKCS9Attribute(String name, Object value)
+ throws IllegalArgumentException {
+ ObjectIdentifier oid = getOID(name);
+
+ if (oid == null)
+ throw new IllegalArgumentException(
+ "Unrecognized attribute name " + name +
+ " constructing PKCS9Attribute.");
+
+ init(oid, value);
+ }
+
+ private void init(ObjectIdentifier oid, Object value)
+ throws IllegalArgumentException {
+
+ index = indexOf(oid, PKCS9_OIDS, 1);
+
+ if (index == -1)
+ throw new IllegalArgumentException(
+ "Unsupported OID " + oid +
+ " constructing PKCS9Attribute.");
+
+ if (!VALUE_CLASSES[index].isInstance(value))
+ throw new IllegalArgumentException(
+ "Wrong value class " +
+ " for attribute " + oid +
+ " constructing PKCS9Attribute; was " +
+ value.getClass().toString() + ", should be " +
+ VALUE_CLASSES[index].toString());
+
+ this.value = value;
+ }
+
+ /**
+ * Construct a PKCS9Attribute from its encoding on an input
+ * stream.
+ *
+ * @exception IOException on parsing error.
+ */
+ public PKCS9Attribute(DerValue derVal) throws IOException {
+
+ decode(derVal);
+ }
+
+ /**
+ * Decode a PKCS9 attribute.
+ *
+ * @param val
+ * the DerValue representing the DER encoding of the attribute.
+ */
+ private void decode(DerValue derVal) throws IOException {
+ DerInputStream derIn = new DerInputStream(derVal.toByteArray());
+ DerValue[] val = derIn.getSequence(2);
+
+ if (derIn.available() != 0)
+ throw new IOException("Excess data parsing PKCS9Attribute");
+
+ if (val.length != 2)
+ throw new IOException("PKCS9Attribute doesn't have two components");
+
+ DerValue[] elems;
+
+ // get the oid
+ ObjectIdentifier oid = val[0].getOID();
+
+ index = indexOf(oid, PKCS9_OIDS, 1);
+ Byte tag;
+
+ if (index == -1)
+ throw new IOException("Invalid OID for PKCS9 attribute: " +
+ oid);
+
+ elems = new DerInputStream(val[1].toByteArray()).getSet(1);
+
+ // check single valued have only one value
+ if (SINGLE_VALUED[index] && elems.length > 1)
+ throwSingleValuedException();
+
+ // check for illegal element tags
+ for (int i = 0; i < elems.length; i++) {
+ tag = Byte.valueOf(elems[i].tag);
+
+ if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1)
+ throwTagException(tag);
+ }
+
+ switch (index) {
+ case 1: // email address
+ case 2: // unstructured name
+ case 8: // unstructured address
+ { // open scope
+ String[] values = new String[elems.length];
+
+ for (int i = 0; i < elems.length; i++)
+ values[i] = elems[i].getAsString();
+ value = values;
+ } // close scope
+ break;
+
+ case 3: // content type
+ value = elems[0].getOID();
+ break;
+
+ case 4: // message digest
+ value = elems[0].getOctetString();
+ break;
+
+ case 5: // signing time
+ value = (new DerInputStream(elems[0].toByteArray())).getUTCTime();
+ break;
+
+ case 6: // countersignature
+ { // open scope
+ SignerInfo[] values = new SignerInfo[elems.length];
+ for (int i = 0; i < elems.length; i++)
+ values[i] =
+ new SignerInfo(elems[i].toDerInputStream());
+ value = values;
+ } // close scope
+ break;
+
+ case 7: // challenge password
+ value = elems[0].getAsString();
+ break;
+
+ case 9: // extended-certificate attribute -- not
+ // supported
+ throw new IOException("PKCS9 extended-certificate " +
+ "attribute not supported.");
+
+ case 10: // IssuerAndSerialNumber attribute -- not
+ // supported
+ throw new IOException("PKCS9 IssuerAndSerialNumber " +
+ "attribute not supported.");
+
+ case 11: // passwordCheck attribute -- not
+ // supported
+ throw new IOException("PKCS9 passwordCheck " +
+ "attribute not supported.");
+ case 12: // PublicKey attribute -- not
+ // supported
+ throw new IOException("PKCS9 PublicKey " +
+ "attribute not supported.");
+ case 13: // SigningDescription attribute -- not
+ // supported
+ throw new IOException("PKCS9 SigningDescription " +
+ "attribute not supported.");
+ case 14: // ExtensionRequest attribute
+ value =
+ new CertificateExtensions(elems[0].toDerInputStream());
+
+ // break unnecessary
+
+ default: // can't happen
+ }
+
+ }
+
+ /**
+ * Write the DER encoding of this attribute to an output stream.
+ *
+ * <P>
+ * N.B.: This method always encodes values of ChallengePassword and UnstructuredAddress attributes as ASN.1
+ * <code>PrintableString</code>s, without checking whether they should be encoded as <code>T61String</code>s.
+ */
+ public void derEncode(OutputStream out) throws IOException {
+ DerOutputStream temp = new DerOutputStream();
+ temp.putOID(getOID());
+ switch (index) {
+ case 1: // email address
+ case 2: // unstructured name
+ { // open scope
+ String[] values = (String[]) value;
+ DerOutputStream[] temps = new
+ DerOutputStream[values.length];
+
+ for (int i = 0; i < values.length; i++) {
+ temps[i] = new DerOutputStream();
+
+ temps[i].putIA5String(values[i]);
+ }
+ temp.putOrderedSetOf(DerValue.tag_Set, temps);
+ } // close scope
+ break;
+
+ case 3: // content type
+ {
+ DerOutputStream temp2 = new DerOutputStream();
+ temp2.putOID((ObjectIdentifier) value);
+ temp.write(DerValue.tag_Set, temp2.toByteArray());
+ }
+ break;
+
+ case 4: // message digest
+ {
+ DerOutputStream temp2 = new DerOutputStream();
+ temp2.putOctetString((byte[]) value);
+ temp.write(DerValue.tag_Set, temp2.toByteArray());
+ }
+ break;
+
+ case 5: // signing time
+ {
+ DerOutputStream temp2 = new DerOutputStream();
+ temp2.putUTCTime((Date) value);
+ temp.write(DerValue.tag_Set, temp2.toByteArray());
+ }
+ break;
+
+ case 6: // countersignature
+ temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value);
+ break;
+
+ case 7: // challenge password
+ {
+ DerOutputStream temp2 = new DerOutputStream();
+ temp2.putPrintableString((String) value);
+ temp.write(DerValue.tag_Set, temp2.toByteArray());
+ }
+ break;
+
+ case 8: // unstructured address
+ { // open scope
+ String[] values = (String[]) value;
+ DerOutputStream[] temps = new
+ DerOutputStream[values.length];
+
+ for (int i = 0; i < values.length; i++) {
+ temps[i] = new DerOutputStream();
+
+ temps[i].putPrintableString(values[i]);
+ }
+ temp.putOrderedSetOf(DerValue.tag_Set, temps);
+ } // close scope
+ break;
+
+ case 9: // extended-certificate attribute -- not
+ // supported
+ throw new IOException("PKCS9 extended-certificate " +
+ "attribute not supported.");
+
+ case 10: // IssuerAndSerialNumber attribute -- not
+ // supported
+ throw new IOException("PKCS9 IssuerAndSerialNumber " +
+ "attribute not supported.");
+
+ case 11: // passwordCheck attribute -- not
+ // supported
+ throw new IOException("PKCS9 passwordCheck " +
+ "attribute not supported.");
+ case 12: // PublicKey attribute -- not
+ // supported
+ throw new IOException("PKCS9 PublicKey " +
+ "attribute not supported.");
+ case 13: // SigningDescription attribute -- not
+ // supported
+ throw new IOException("PKCS9 SigningDescription " +
+ "attribute not supported.");
+ case 14: // ExtensionRequest attribute
+ try {
+ DerOutputStream temp2 = new DerOutputStream();
+ //temp2.putSequence((CertificateExtensions) value);
+ ((CertificateExtensions) value).encode(temp2);
+ temp.write(DerValue.tag_Sequence, temp2.toByteArray());
+ } catch (CertificateException e) {
+ throw new IOException("PKCS9 extension attributes not encoded");
+ }
+
+ // break unnecessary
+ default: // can't happen
+ }
+
+ DerOutputStream derOut = new DerOutputStream();
+ derOut.write(DerValue.tag_Sequence, temp.toByteArray());
+
+ out.write(derOut.toByteArray());
+
+ }
+
+ /**
+ * Get the value of this attribute. If the attribute is
+ * single-valued, return just the one value. If the attribute is
+ * multiple-valued, return an array containing all the values.
+ * It is possible for this array to be of length 0.
+ *
+ * <P>
+ * The following table gives the class of the value returned, depending on the type of this attribute.
+ *
+ * <P>
+ * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
+ *
+ * <TR>
+ * <TH>OID</TH>
+ * <TH>Attribute Type Name</TH>
+ * <TH>Kind</TH>
+ * <TH>Value Class</TH>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.1</TD>
+ * <TD>EmailAddress</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.2</TD>
+ * <TD>UnstructuredName</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.3</TD>
+ * <TD>ContentType</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>ObjectIdentifier</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.4</TD>
+ * <TD>MessageDigest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>byte[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.5</TD>
+ * <TD>SigningTime</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Date</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.6</TD>
+ * <TD>Countersignature</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD><code>SignerInfo[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.7</TD>
+ * <TD>ChallengePassword</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.8</TD>
+ * <TD>UnstructuredAddress</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>String[]</code></TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.9</TD>
+ * <TD>ExtendedCertificateAttributes</TD>
+ * <TD>Multiple-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.10</TD>
+ * <TD>IssuerAndSerialNumber</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.11</TD>
+ * <TD>PasswordCheck</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.12</TD>
+ * <TD>PublicKey</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.13</TD>
+ * <TD>SigningDescription</TD>
+ * <TD>Single-valued</TD>
+ * <TD>(not supported)</TD>
+ * </TR>
+ *
+ * <TR>
+ * <TD>1.2.840.113549.1.9.14</TD>
+ * <TD>ExtensionRequest</TD>
+ * <TD>Single-valued</TD>
+ * <TD><code>Sequence</code></TD>
+ * </TR>
+ *
+ * </TABLE>
+ *
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Show whether this attribute is single-valued.
+ */
+ public boolean isSingleValued() {
+ return SINGLE_VALUED[index];
+ }
+
+ /**
+ * Return the OID of this attribute.
+ */
+ public ObjectIdentifier getOID() {
+ return PKCS9_OIDS[index];
+ }
+
+ /**
+ * Return the name of this attribute.
+ */
+ public String getName() {
+ return (String) OID_NAME_TABLE.get(PKCS9_OIDS[index]);
+ }
+
+ /**
+ * Return the OID for a given attribute name or null if we don't recognize
+ * the name.
+ */
+ public static ObjectIdentifier getOID(String name) {
+ return (ObjectIdentifier) NAME_OID_TABLE.get(name.toLowerCase());
+ }
+
+ /**
+ * Return the attribute name for a given OID or null if we don't recognize
+ * the oid.
+ */
+ public static String getName(ObjectIdentifier oid) {
+ return (String) OID_NAME_TABLE.get(oid);
+ }
+
+ /**
+ * Returns a string representation of this attribute.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer(100);
+
+ buf.append("[");
+
+ buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index]));
+ buf.append(": ");
+
+ if (SINGLE_VALUED[index]) {
+ if (value instanceof byte[]) { // special case for octet string
+ netscape.security.util.PrettyPrintFormat pp =
+ new netscape.security.util.PrettyPrintFormat(" ", 20);
+ String valuebits = pp.toHexString(((byte[]) value));
+ buf.append(valuebits);
+ } else {
+ buf.append(value.toString());
+ }
+ buf.append("]");
+ return buf.toString();
+ } else { // multiple-valued
+ boolean first = true;
+ Object[] values = (Object[]) value;
+
+ for (int j = 0; j < values.length; j++) {
+ if (first)
+ first = false;
+ else
+ buf.append(", ");
+
+ buf.append(values[j].toString());
+ }
+ return buf.toString();
+ }
+ }
+
+ /**
+ * Beginning the search at <code>start</code>, find the first
+ * index <code>i</code> such that <code>a[i] = obj</code>.
+ *
+ * @return the index, if found, and -1 otherwise.
+ */
+ static int indexOf(Object obj, Object[] a, int start) {
+ for (int i = start; i < a.length; i++) {
+ if (obj.equals(a[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Throw an exception when there are multiple values for
+ * a single-valued attribute.
+ */
+ private void throwSingleValuedException() throws IOException {
+ throw new IOException("Single-value attribute " +
+ getOID() + " (" + getName() + ")" +
+ " has multiple values.");
+ }
+
+ /**
+ * Throw an exception when the tag on a value encoding is
+ * wrong for the attribute whose value it is.
+ */
+ private void throwTagException(Byte tag)
+ throws IOException {
+ Byte[] expectedTags = PKCS9_VALUE_TAGS[index];
+ StringBuffer msg = new StringBuffer(100);
+ msg.append("Value of attribute ");
+ msg.append(getOID().toString());
+ msg.append(" (");
+ msg.append(getName());
+ msg.append(") has wrong tag: ");
+ msg.append(tag.toString());
+ msg.append(". Expected tags: ");
+
+ msg.append(expectedTags[0].toString());
+
+ for (int i = 1; i < expectedTags.length; i++) {
+ msg.append(", ");
+ msg.append(expectedTags[i].toString());
+ }
+ msg.append(".");
+ throw new IOException(msg.toString());
+ }
+
+}
diff --git a/base/util/src/netscape/security/pkcs/PKCS9Attributes.java b/base/util/src/netscape/security/pkcs/PKCS9Attributes.java
new file mode 100644
index 000000000..485cdded1
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/PKCS9Attributes.java
@@ -0,0 +1,312 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Hashtable;
+
+import netscape.security.util.DerEncoder;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+
+/**
+ * A set of attributes of class PKCS9Attribute.
+ *
+ * @version 1.2 97/12/10
+ * @author Douglas Hoover
+ */
+public class PKCS9Attributes {
+
+ /**
+ * Attributes in this set indexed by OID.
+ */
+ private final Hashtable<ObjectIdentifier, PKCS9Attribute> attributes = new Hashtable<ObjectIdentifier, PKCS9Attribute>(3);
+
+ /**
+ * The keys of this hashtable are the OIDs of permitted attributes.
+ */
+ private final Hashtable<ObjectIdentifier, ObjectIdentifier> permittedAttributes;
+
+ /**
+ * The DER encoding of this attribute set. The tag byte must be
+ * DerValue.tag_SetOf.
+ */
+ private final byte[] derEncoding;
+
+ /**
+ * Construct a set of PKCS9 Attributes from its
+ * DER encoding on a DerInputStream, accepting only attributes
+ * with OIDs on the given
+ * list. If the array is null, accept all attributes supported by
+ * class PKCS9Attribute.
+ *
+ * @param permittedAttributes
+ * Array of attribute OIDs that will be accepted.
+ * @param buf
+ * the contents of the DER encoding of the attribute set.
+ *
+ * @exception IOException
+ * on i/o error, encoding syntax error, unacceptable or
+ * unsupported attribute, or duplicate attribute.
+ *
+ * @see PKCS9Attribute
+ */
+ public PKCS9Attributes(ObjectIdentifier[] permittedAttributes,
+ DerInputStream in) throws IOException {
+ if (permittedAttributes != null) {
+ this.permittedAttributes =
+ new Hashtable<ObjectIdentifier, ObjectIdentifier>(permittedAttributes.length);
+
+ for (int i = 0; i < permittedAttributes.length; i++)
+ this.permittedAttributes.put(permittedAttributes[i],
+ permittedAttributes[i]);
+ } else {
+ this.permittedAttributes = null;
+ }
+
+ // derEncoding initialized in <code>decode()</code>
+ derEncoding = decode(in);
+ }
+
+ /**
+ * Construct a set of PKCS9 Attributes from its contents of its
+ * DER encoding on a DerInputStream. Accept all attributes
+ * supported by class PKCS9Attribute.
+ *
+ * @exception IOException
+ * on i/o error, encoding syntax error, or unsupported or
+ * duplicate attribute.
+ *
+ * @see PKCS9Attribute
+ */
+ public PKCS9Attributes(DerInputStream in) throws IOException {
+ // anything goes
+ // derEncoding initialized in <code>decode()</code>
+ derEncoding = decode(in);
+ permittedAttributes = null;
+ }
+
+ /**
+ * Construct a set of PKCS9 Attributes from the given array of
+ * PCK9 attributes.
+ * DER encoding on a DerInputStream. All attributes in <code>attribs</code> must be
+ * supported by class PKCS9Attribute.
+ *
+ * @exception IOException
+ * on i/o error, encoding syntax error, or unsupported or
+ * duplicate attribute.
+ *
+ * @see PKCS9Attribute
+ */
+ public PKCS9Attributes(PKCS9Attribute[] attribs)
+ throws IllegalArgumentException, IOException {
+ ObjectIdentifier oid;
+ for (int i = 0; i < attribs.length; i++) {
+ oid = attribs[i].getOID();
+ if (attributes.containsKey(oid))
+ throw new IllegalArgumentException(
+ "PKCSAttribute " + attribs[i].getOID() +
+ " duplicated while constructing " +
+ "PKCS9Attributes.");
+
+ attributes.put(oid, attribs[i]);
+ }
+ derEncoding = generateDerEncoding();
+ permittedAttributes = null;
+ }
+
+ /**
+ * Decode this set of PKCS9 attribute set from the contents of its
+ * DER encoding.
+ *
+ * @param buf
+ * the contents of the DER encoding of the attribute set.
+ *
+ * @exception IOException
+ * on i/o error, encoding syntax error, unacceptable or
+ * unsupported attribute, or duplicate attribute.
+ */
+ private byte[] decode(DerInputStream in) throws IOException {
+
+ DerValue val = in.getDerValue();
+
+ // save the DER encoding with its proper tag byte.
+ byte[] derEncoding = val.toByteArray();
+ derEncoding[0] = DerValue.tag_SetOf;
+
+ DerInputStream derIn = new DerInputStream(derEncoding);
+ DerValue[] derVals = derIn.getSet(3, true);
+
+ PKCS9Attribute attrib;
+ ObjectIdentifier oid;
+
+ for (int i = 0; i < derVals.length; i++) {
+ attrib = new PKCS9Attribute(derVals[i]);
+ oid = attrib.getOID();
+
+ if (attributes.get(oid) != null)
+ throw new IOException("Duplicate PKCS9 attribute: " + oid);
+
+ if (permittedAttributes != null &&
+ !permittedAttributes.containsKey(oid))
+ throw new IOException("Attribute " + oid +
+ " not permitted in this attribute set");
+
+ attributes.put(oid, attrib);
+ }
+ return derEncoding;
+
+ }
+
+ /**
+ * Put the DER encoding of this PKCS9 attribute set on an
+ * DerOutputStream, tagged with the given implicit tag.
+ *
+ * @param tag the implicit tag to use in the DER encoding.
+ * @param out the output stream on which to put the DER encoding.
+ *
+ * @exception IOException on output error.
+ */
+ public void encode(byte tag, OutputStream out) throws IOException {
+ out.write(tag);
+ out.write(derEncoding, 1, derEncoding.length - 1);
+ }
+
+ private byte[] generateDerEncoding() throws IOException {
+ DerOutputStream out = new DerOutputStream();
+ Object[] attribVals = attributes.values().toArray();
+
+ out.putOrderedSetOf(DerValue.tag_SetOf,
+ castToDerEncoder(attribVals));
+ return out.toByteArray();
+ }
+
+ /**
+ * Return the DER encoding of this attribute set, tagged with
+ * DerValue.tag_SetOf.
+ */
+ public byte[] getDerEncoding() throws IOException {
+ return (byte[]) derEncoding.clone();
+
+ }
+
+ /**
+ * Get an attribute from this set.
+ */
+ public PKCS9Attribute getAttribute(ObjectIdentifier oid) {
+ return attributes.get(oid);
+ }
+
+ /**
+ * Get an attribute from this set.
+ */
+ public PKCS9Attribute getAttribute(String name) {
+ return attributes.get(PKCS9Attribute.getOID(name));
+ }
+
+ /**
+ * Get an array of all attributes in this set, in order of OID.
+ */
+ public PKCS9Attribute[] getAttributes() {
+ PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()];
+
+ int j = 0;
+ for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length &&
+ j < attribs.length; i++) {
+ attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
+
+ if (attribs[j] != null)
+ j++;
+ }
+ return attribs;
+ }
+
+ /**
+ * Get an attribute value by OID.
+ */
+ public Object getAttributeValue(ObjectIdentifier oid)
+ throws IOException {
+ try {
+ Object value = getAttribute(oid).getValue();
+ return value;
+ } catch (NullPointerException ex) {
+ throw new IOException("No value found for attribute " + oid);
+ }
+
+ }
+
+ /**
+ * Get an attribute value by type name.
+ */
+ public Object getAttributeValue(String name) throws IOException {
+ ObjectIdentifier oid = PKCS9Attribute.getOID(name);
+
+ if (oid == null)
+ throw new IOException("Attribute name " + name +
+ " not recognized or not supported.");
+
+ return getAttributeValue(oid);
+ }
+
+ /**
+ * Returns the PKCS9 block in a printable string form.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer(200);
+ buf.append("PKCS9 Attributes: [\n\t");
+
+ PKCS9Attribute value;
+
+ boolean first = true;
+ for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) {
+ value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
+
+ if (value == null)
+ continue;
+
+ // we have a value; print it
+ if (first)
+ first = false;
+ else
+ buf.append(";\n\t");
+
+ buf.append(value.toString());
+ }
+
+ buf.append("\n\t] (end PKCS9 Attributes)");
+
+ return buf.toString();
+ }
+
+ /**
+ * Cast an object array whose components are <code>DerEncoder</code>s to <code>DerEncoder[]</code>.
+ */
+ static DerEncoder[] castToDerEncoder(Object[] objs) {
+
+ DerEncoder[] encoders = new DerEncoder[objs.length];
+
+ for (int i = 0; i < encoders.length; i++)
+ encoders[i] = (DerEncoder) objs[i];
+
+ return encoders;
+ }
+
+}
diff --git a/base/util/src/netscape/security/pkcs/ParsingException.java b/base/util/src/netscape/security/pkcs/ParsingException.java
new file mode 100644
index 000000000..88e91a8db
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/ParsingException.java
@@ -0,0 +1,35 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+
+public class ParsingException extends IOException {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8135726194372647410L;
+
+ public ParsingException() {
+ super();
+ }
+
+ public ParsingException(String s) {
+ super(s);
+ }
+}
diff --git a/base/util/src/netscape/security/pkcs/SignerInfo.java b/base/util/src/netscape/security/pkcs/SignerInfo.java
new file mode 100644
index 000000000..adb0115cc
--- /dev/null
+++ b/base/util/src/netscape/security/pkcs/SignerInfo.java
@@ -0,0 +1,347 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+
+import netscape.security.util.BigInt;
+import netscape.security.util.DerEncoder;
+import netscape.security.util.DerInputStream;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.X500Name;
+
+/**
+ * A SignerInfo, as defined in PKCS#7's signedData type.
+ *
+ * @author Benjamin Renaud
+ * @version 1.27 97/12/10
+ */
+public class SignerInfo implements DerEncoder {
+
+ BigInt version;
+ X500Name issuerName;
+ BigInt certificateSerialNumber;
+ AlgorithmId digestAlgorithmId;
+ AlgorithmId digestEncryptionAlgorithmId;
+ byte[] encryptedDigest;
+
+ PKCS9Attributes authenticatedAttributes;
+ PKCS9Attributes unauthenticatedAttributes;
+
+ public SignerInfo(X500Name issuerName,
+ BigInt serial,
+ AlgorithmId digestAlgorithmId,
+ AlgorithmId digestEncryptionAlgorithmId,
+ byte[] encryptedDigest) {
+ this.version = new BigInt(1);
+ this.issuerName = issuerName;
+ this.certificateSerialNumber = serial;
+ this.digestAlgorithmId = digestAlgorithmId;
+ this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
+ this.encryptedDigest = encryptedDigest;
+ }
+
+ public SignerInfo(X500Name issuerName,
+ BigInt serial,
+ AlgorithmId digestAlgorithmId,
+ PKCS9Attributes authenticatedAttributes,
+ AlgorithmId digestEncryptionAlgorithmId,
+ byte[] encryptedDigest,
+ PKCS9Attributes unauthenticatedAttributes) {
+ this.version = new BigInt(1);
+ this.issuerName = issuerName;
+ this.certificateSerialNumber = serial;
+ this.digestAlgorithmId = digestAlgorithmId;
+ this.authenticatedAttributes = authenticatedAttributes;
+ this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
+ this.encryptedDigest = encryptedDigest;
+ this.unauthenticatedAttributes = unauthenticatedAttributes;
+ }
+
+ public SignerInfo(DerInputStream derin)
+ throws IOException, ParsingException {
+
+ // version
+ version = derin.getInteger();
+
+ // issuerAndSerialNumber
+ DerValue[] issuerAndSerialNumber = derin.getSequence(2);
+ byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();
+ issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,
+ issuerBytes));
+ certificateSerialNumber = issuerAndSerialNumber[1].getInteger();
+
+ // digestAlgorithmId
+ DerValue tmp = derin.getDerValue();
+
+ digestAlgorithmId = AlgorithmId.parse(tmp);
+
+ /*
+ * check if set of auth attributes (implicit tag) is provided
+ * (auth attributes are OPTIONAL)
+ */
+ if ((byte) (derin.peekByte()) == (byte) 0xA0) {
+ authenticatedAttributes = new PKCS9Attributes(derin);
+ }
+
+ // digestEncryptionAlgorithmId - little RSA naming scheme -
+ // signature == encryption...
+ tmp = derin.getDerValue();
+
+ digestEncryptionAlgorithmId = AlgorithmId.parse(tmp);
+
+ // encryptedDigest
+ encryptedDigest = derin.getOctetString();
+
+ /*
+ * check if set of unauth attributes (implicit tag) is provided
+ * (unauth attributes are OPTIONAL)
+ */
+ if (derin.available() != 0 && (byte) (derin.peekByte()) == (byte) 0xA1) {
+ unauthenticatedAttributes = new PKCS9Attributes(derin);
+ }
+
+ // all done
+ if (derin.available() != 0) {
+ throw new ParsingException("extra data at the end");
+ }
+ }
+
+ public void encode(DerOutputStream out) throws IOException {
+
+ derEncode(out);
+ }
+
+ /**
+ * DER encode this object onto an output stream.
+ * Implements the <code>DerEncoder</code> interface.
+ *
+ * @param out
+ * the output stream on which to write the DER encoding.
+ *
+ * @exception IOException on encoding error.
+ */
+ public void derEncode(OutputStream out) throws IOException {
+ DerOutputStream seq = new DerOutputStream();
+ seq.putInteger(version);
+ DerOutputStream issuerAndSerialNumber = new DerOutputStream();
+ issuerName.encode(issuerAndSerialNumber);
+ issuerAndSerialNumber.putInteger(certificateSerialNumber);
+ seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);
+
+ digestAlgorithmId.encode(seq);
+
+ // encode authenticated attributes if there are any
+ if (authenticatedAttributes != null)
+ authenticatedAttributes.encode((byte) 0xA0, seq);
+
+ digestEncryptionAlgorithmId.encode(seq);
+
+ seq.putOctetString(encryptedDigest);
+
+ // encode unauthenticated attributes if there are any
+ if (unauthenticatedAttributes != null)
+ unauthenticatedAttributes.encode((byte) 0xA1, seq);
+
+ DerOutputStream tmp = new DerOutputStream();
+ tmp.write(DerValue.tag_Sequence, seq);
+
+ out.write(tmp.toByteArray());
+ }
+
+ public X509Certificate getCertificate(PKCS7 block)
+ throws IOException {
+ return block.getCertificate(certificateSerialNumber, issuerName);
+ }
+
+ /* Returns null if verify fails, this signerInfo if
+ verify succeeds. */
+ SignerInfo verify(PKCS7 block, byte[] data)
+ throws NoSuchAlgorithmException, SignatureException {
+
+ try {
+
+ ContentInfo content = block.getContentInfo();
+ if (data == null) {
+ data = content.getContentBytes();
+ }
+
+ String digestAlgname =
+ getDigestAlgorithmId().getName();
+
+ byte[] dataSigned;
+
+ // if there are authenticate attributes, get the message
+ // digest and compare it with the digest of data
+ if (authenticatedAttributes == null) {
+ dataSigned = data;
+ } else {
+
+ // first, check content type
+ ObjectIdentifier contentType = (ObjectIdentifier)
+ authenticatedAttributes.getAttributeValue(
+ PKCS9Attribute.CONTENT_TYPE_OID);
+ if (contentType == null ||
+ !contentType.equals(content.contentType))
+ return null; // contentType does not match, bad SignerInfo
+
+ // now, check message digest
+ byte[] messageDigest = (byte[])
+ authenticatedAttributes.getAttributeValue(
+ PKCS9Attribute.MESSAGE_DIGEST_OID);
+
+ if (messageDigest == null) // fail if there is no message digest
+ return null;
+
+ MessageDigest md = MessageDigest.getInstance(digestAlgname);
+ byte[] computedMessageDigest = md.digest(data);
+
+ if (messageDigest.length != computedMessageDigest.length)
+ return null;
+ for (int i = 0; i < messageDigest.length; i++) {
+ if (messageDigest[i] != computedMessageDigest[i])
+ return null;
+ }
+
+ // message digest attribute matched
+ // digest of original data
+
+ // the data actually signed is the DER encoding of
+ // the authenticated attributes (tagged with
+ // the "SET OF" tag, not 0xA0).
+ dataSigned = authenticatedAttributes.getDerEncoding();
+ }
+
+ // put together digest algorithm and encryption algorithm
+ // to form signing algorithm
+ String encryptionAlgname =
+ getDigestEncryptionAlgorithmId().getName();
+
+ String algname;
+ if (encryptionAlgname.equals("DSA") ||
+ encryptionAlgname.equals("SHA1withDSA")) {
+ algname = "DSA";
+ } else {
+ algname = digestAlgname + "/" + encryptionAlgname;
+ }
+
+ Signature sig = Signature.getInstance(algname);
+ X509Certificate cert = getCertificate(block);
+
+ if (cert == null) {
+ return null;
+ }
+
+ PublicKey key = cert.getPublicKey();
+ sig.initVerify(key);
+
+ sig.update(dataSigned);
+
+ if (sig.verify(encryptedDigest)) {
+ return this;
+ }
+
+ } catch (IOException e) {
+ throw new SignatureException("IO error verifying signature:\n" +
+ e.getMessage());
+
+ } catch (InvalidKeyException e) {
+ throw new SignatureException("InvalidKey: " + e.getMessage());
+
+ }
+ return null;
+ }
+
+ /* Verify the content of the pkcs7 block. */
+ SignerInfo verify(PKCS7 block)
+ throws NoSuchAlgorithmException, SignatureException {
+ return verify(block, null);
+ }
+
+ public BigInt getVersion() {
+ return version;
+ }
+
+ public X500Name getIssuerName() {
+ return issuerName;
+ }
+
+ public BigInt getCertificateSerialNumber() {
+ return certificateSerialNumber;
+ }
+
+ public AlgorithmId getDigestAlgorithmId() {
+ return digestAlgorithmId;
+ }
+
+ public PKCS9Attributes getAuthenticatedAttributes() {
+ return authenticatedAttributes;
+ }
+
+ public AlgorithmId getDigestEncryptionAlgorithmId() {
+ return digestEncryptionAlgorithmId;
+ }
+
+ public byte[] getEncryptedDigest() {
+ return encryptedDigest;
+ }
+
+ public PKCS9Attributes getUnauthenticatedAttributes() {
+ return unauthenticatedAttributes;
+ }
+
+ public String toString() {
+ netscape.security.util.PrettyPrintFormat pp =
+ new netscape.security.util.PrettyPrintFormat(" ", 20);
+ String digestbits = pp.toHexString(encryptedDigest);
+
+ String out = "";
+
+ out += "Signer Info for (issuer): " + issuerName + "\n";
+ out += "\tversion: " + version + "\n";
+ out += "\tcertificateSerialNumber: " + certificateSerialNumber +
+ "\n";
+ out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";
+ if (authenticatedAttributes != null) {
+ out += "\tauthenticatedAttributes: " + authenticatedAttributes +
+ "\n";
+ }
+ out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId +
+ "\n";
+
+ out += "\tencryptedDigest: " + "\n" +
+ digestbits + "\n";
+ if (unauthenticatedAttributes != null) {
+ out += "\tunauthenticatedAttributes: " +
+ unauthenticatedAttributes + "\n";
+ }
+ return out;
+ }
+
+}