diff options
author | Michael Koch <konqueror@gmx.de> | 2003-04-30 07:23:42 +0000 |
---|---|---|
committer | Michael Koch <mkoch@gcc.gnu.org> | 2003-04-30 07:23:42 +0000 |
commit | 43905ff30b187d8d1d1dee50e722f47fd8a93144 (patch) | |
tree | 89373f6e7f4c07ae0c9c6eeebf05fcb7bbcdfa36 /libjava/gnu/java/security | |
parent | 505b0fd661e21182856cfcf73ed6f39c707ac28b (diff) | |
download | gcc-43905ff30b187d8d1d1dee50e722f47fd8a93144.zip gcc-43905ff30b187d8d1d1dee50e722f47fd8a93144.tar.gz gcc-43905ff30b187d8d1d1dee50e722f47fd8a93144.tar.bz2 |
Engine.java, [...]: New files from classpath.
2003-04-30 Michael Koch <konqueror@gmx.de>
* gnu/java/security/Engine.java,
gnu/java/security/OID.java,
gnu/java/security/der/BitString.java,
gnu/java/security/der/DER.java,
gnu/java/security/der/DERReader.java,
gnu/java/security/der/DERValue.java,
gnu/java/security/der/DERWriter.java,
gnu/java/security/provider/DSAKeyFactory.java,
gnu/java/security/provider/X509CertificateFactory.java,
gnu/java/security/x509/X500DistinguishedName.java,
gnu/java/security/x509/X509CRL.java,
gnu/java/security/x509/X509CRLEntry.java,
gnu/java/security/x509/X509Certificate.java,
java/security/cert/CRLSelector.java,
java/security/cert/CertPathBuilder.java,
java/security/cert/CertPathBuilderResult.java,
java/security/cert/CertPathBuilderSpi.java,
java/security/cert/CertPathParameters.java,
java/security/cert/CertPathValidator.java,
java/security/cert/CertPathValidatorResult.java,
java/security/cert/CertPathValidatorSpi.java,
java/security/cert/CertSelector.java,
java/security/cert/CertStore.java,
java/security/cert/CertStoreParameters.java,
java/security/cert/CertStoreSpi.java,
java/security/cert/CollectionCertStoreParameters.java,
java/security/cert/LDAPCertStoreParameters.java,
java/security/cert/PKIXBuilderParameters.java,
java/security/cert/PKIXCertPathBuilderResult.java,
java/security/cert/PKIXCertPathChecker.java,
java/security/cert/PKIXCertPathValidatorResult.java,
java/security/cert/PKIXParameters.java,
java/security/cert/PolicyNode.java,
java/security/cert/PolicyQualifierInfo.java,
java/security/cert/TrustAnchor.java,
javax/security/auth/x500/X500Principal.java:
New files from classpath.
* gnu/java/io/ASN1ParsingException.java,
gnu/java/io/Base64InputStream.java,
gnu/java/security/der/DEREncodingException.java,
gnu/java/security/provider/DSAParameters.java,
gnu/java/security/provider/DSASignature.java,
gnu/java/security/provider/Gnu.java,
gnu/java/security/provider/GnuDSAPrivateKey.java,
gnu/java/security/provider/GnuDSAPublicKey.java,
java/security/AlgorithmParameterGenerator.java,
java/security/AlgorithmParameters.java,
java/security/KeyFactory.java,
java/security/KeyPairGenerator.java,
java/security/KeyStore.java,
java/security/MessageDigest.java,
java/security/SecureClassLoader.java,
java/security/SecureRandom.java,
java/security/Security.java,
java/security/Signature.java,
java/security/cert/Certificate.java,
java/security/cert/CertificateFactory.java,
java/security/cert/CertificateFactorySpi.java,
java/security/cert/X509CRL.java,
java/security/cert/X509Certificate.java,
java/security/spec/DSAPublicKeySpec.java:
New versions from classpath.
* gnu/java/security/provider/DERReader.java,
gnu/java/security/provider/DERWriter.java,
java/security/Engine.java: Removed.
* Makefile.am
(java_source_files, javax_source_files): Added new files.
* Makefile.in: Regenerated.
From-SVN: r66283
Diffstat (limited to 'libjava/gnu/java/security')
21 files changed, 4694 insertions, 331 deletions
diff --git a/libjava/gnu/java/security/Engine.java b/libjava/gnu/java/security/Engine.java new file mode 100644 index 0000000..9f6568c --- /dev/null +++ b/libjava/gnu/java/security/Engine.java @@ -0,0 +1,237 @@ +/* Engine -- generic getInstance method. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.security; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; + +/** + * Generic implementation of the getInstance methods in the various + * engine classes in java.security. + * <p> + * These classes ({@link java.security.Signature} for example) can be + * thought of as the "chrome, upholstery, and steering wheel", and the SPI + * (service provider interface, e.g. {@link java.security.SignatureSpi}) + * classes can be thought of as the "engine" -- providing the actual + * functionality of whatever cryptographic algorithm the instance + * represents. + * + * @see Provider + * @author Casey Marshall + */ +public final class Engine +{ + + // Constants. + // ------------------------------------------------------------------------ + + /** Prefix for aliases. */ + private static final String ALG_ALIAS = "Alg.Alias."; + + /** Maximum number of aliases to try. */ + private static final int MAX_ALIASES = 5; + + /** Argument list for no-argument constructors. */ + private static final Object[] NO_ARGS = new Object[0]; + + // Constructor. + // ------------------------------------------------------------------------ + + /** This class cannot be instantiated. */ + private Engine() { } + + // Class method. + // ------------------------------------------------------------------------ + + /** + * Get the implementation for <i>algorithm</i> for service + * <i>service</i> from <i>provider</i>. The service is e.g. + * "Signature", and the algorithm "DSA". + * + * @param service The service name. + * @param algorithm The name of the algorithm to get. + * @param provider The provider to get the implementation from. + * @return The engine class for the specified algorithm; the object + * returned is typically a subclass of the SPI class for that + * service, but callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be + * found or cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor + * throws an exception. + * @throws IllegalArgumentException If any of the three arguments are null. + */ + public static Object getInstance(String service, String algorithm, + Provider provider) + throws InvocationTargetException, NoSuchAlgorithmException + { + return getInstance(service, algorithm, provider, NO_ARGS); + } + + /** + * Get the implementation for <i>algorithm</i> for service + * <i>service</i> from <i>provider</i>, passing <i>initArgs</i> to the + * SPI class's constructor (which cannot be null; pass a zero-length + * array if the SPI takes no arguments). The service is e.g. + * "Signature", and the algorithm "DSA". + * + * @param service The service name. + * @param algorithm The name of the algorithm to get. + * @param provider The provider to get the implementation from. + * @param initArgs The arguments to pass to the SPI class's + * constructor (cannot be null). + * @return The engine class for the specified algorithm; the object + * returned is typically a subclass of the SPI class for that + * service, but callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be + * found or cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor + * throws an exception. + * @throws IllegalArgumentException If any of the four arguments are null. + */ + public static Object getInstance(String service, String algorithm, + Provider provider, Object[] initArgs) + throws InvocationTargetException, NoSuchAlgorithmException + { + if (service == null || algorithm == null + || provider == null || initArgs == null) + throw new IllegalArgumentException(); + + // If there is no property "service.algorithm" + if (provider.getProperty(service + "." + algorithm) == null) + { + // Iterate through aliases, until we find the class name or resolve + // too many aliases. + String alias = null; + int count = 0; + while ((alias = provider.getProperty( + ALG_ALIAS + service + "." + algorithm)) != null) + { + if (algorithm.equals(alias)) // Refers to itself! + break; + algorithm = alias; + if (count++ > MAX_ALIASES) + throw new NoSuchAlgorithmException("too many aliases"); + } + if (provider.getProperty(service + "." + algorithm) == null) + throw new NoSuchAlgorithmException(algorithm); + } + + // Find and instantiate the implementation. + Class clazz = null; + ClassLoader loader = provider.getClass().getClassLoader(); + Constructor constructor = null; + String error = algorithm; + + try + { + if (loader != null) + clazz = loader.loadClass(provider.getProperty(service+"."+algorithm)); + else + clazz = Class.forName(provider.getProperty(service+"."+algorithm)); + constructor = getCompatibleConstructor(clazz, initArgs); + return constructor.newInstance(initArgs); + } + catch (ClassNotFoundException cnfe) + { + error = "class not found: " + algorithm; + } + catch (IllegalAccessException iae) + { + error = "illegal access: " + iae.getMessage(); + } + catch (InstantiationException ie) + { + error = "instantiation exception: " + ie.getMessage(); + } + catch (ExceptionInInitializerError eiie) + { + error = "exception in initializer: " + eiie.getMessage(); + } + catch (SecurityException se) + { + error = "security exception: " + se.getMessage(); + } + catch (NoSuchMethodException nsme) + { + error = "no appropriate constructor found"; + } + + throw new NoSuchAlgorithmException(error); + } + + // Own methods. + // ------------------------------------------------------------------------ + + /** + * Find a constructor in the given class that can take the specified + * argument list, allowing any of which to be null. + * + * @param clazz The class from which to get the constructor. + * @param initArgs The argument list to be passed to the constructor. + * @return The constructor. + * @throws NoSuchMethodException If no constructor of the given class + * can take the specified argument array. + */ + private static Constructor getCompatibleConstructor(Class clazz, + Object[] initArgs) + throws NoSuchMethodException + { + Constructor[] c = clazz.getConstructors(); + outer:for (int i = 0; i < c.length; i++) + { + Class[] argTypes = c[i].getParameterTypes(); + if (argTypes.length != initArgs.length) + continue; + for (int j = 0; j < argTypes.length; j++) + { + if (initArgs[j] != null && + !argTypes[j].isAssignableFrom(initArgs[j].getClass())) + continue outer; + } + // If we reach this point, we know this constructor (c[i]) has + // the same number of parameters as the target parameter list, + // and all our parameters are either (1) null, or (2) assignable + // to the target parameter type. + return c[i]; + } + throw new NoSuchMethodException(); + } +} diff --git a/libjava/gnu/java/security/OID.java b/libjava/gnu/java/security/OID.java new file mode 100644 index 0000000..c27ec92 --- /dev/null +++ b/libjava/gnu/java/security/OID.java @@ -0,0 +1,511 @@ +/* OID.java -- numeric representation of an object identifier + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.util.StringTokenizer; + +import gnu.java.security.der.DEREncodingException; + +/** + * This immutable class represents an object identifier, or OID. + * + * <p>OIDs are represented as a series of hierarcical tokens, each of + * which is usually represented as a single, unsigned integer. The + * hierarchy works so that later tokens are considered within the group + * of earlier tokens. Thus, the OID for the Serpent block cipher, + * 1.3.6.1.4.1.11591.13.2, is maintained by the GNU project, whose OID + * is 1.3.6.1.4.1.11591 (which is, in turn, part of bigger, more general + * bodies; the topmost, 1, stands for the OIDs assigned by the + * International Standards Organization, ISO). + * + * <p>OIDs can be represented in a variety of ways, including the + * dotted-decimal form we use here. + * + * <p>OIDs may be relative, in which case the first two elements of the + * OID are omitted. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class OID implements Cloneable, Comparable, java.io.Serializable +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** + * The numeric ID structure. + */ + private int[] components; + + /** + * The string representation of this OID, in dotted-decimal format. + */ + private transient String strRep; + + /** + * The DER encoding of this OID. + */ + private transient byte[] der; + + /** + * Whether or not this OID is relative. + */ + private boolean relative; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new OID from the given byte array. The argument (which can + * neither be null nor zero-length) is copied to prevent subsequent + * modification. + * + * @param components The numeric IDs. + * @throws IllegalArgumentException If <i>components</i> is null or empty. + */ + public OID(int[] components) + { + this(components, false); + } + + /** + * Create a new OID from the given byte array. The argument (which can + * neither be null nor zero-length) is copied to prevent subsequent + * modification. + * + * @param components The numeric IDs. + * @param relative The relative flag. + * @throws IllegalArgumentException If <i>components</i> is null or empty. + */ + public OID(int[] components, boolean relative) + { + if (components == null || components.length == 0) + throw new IllegalArgumentException(); + this.components = (int[]) components.clone(); + this.relative = relative; + } + + /** + * Create a new OID from the given dotted-decimal representation. + * + * @param strRep The string representation of the OID. + * @throws IllegalArgumentException If the string does not contain at + * least one integer. + * @throws NumberFormatException If the string does not contain only + * numbers and periods ('.'). + */ + public OID(String strRep) + { + this(strRep, false); + } + + /** + * Create a new OID from the given dotted-decimal representation. + * + * @param strRep The string representation of the OID. + * @param relative The relative flag. + * @throws IllegalArgumentException If the string does not contain at + * least one integer. + * @throws NumberFormatException If the string does not contain only + * numbers and periods ('.'). + */ + public OID(String strRep, boolean relative) + { + this.relative = relative; + this.strRep = strRep; + components = fromString(strRep); + } + + /** + * Construct a new OID from the DER bytes in an input stream. This method + * does not read the tag or the length field from the input stream, so + * the caller must supply the number of octets in this OID's encoded + * form. + * + * @param derIn The DER input stream. + * @param len The number of bytes in the encoded form. + * @throws IOException If an error occurs reading the OID. + */ + public OID(InputStream derIn, int len) throws IOException + { + this(derIn, len, false); + } + + /** + * Construct a new OID from the DER bytes in an input stream. This method + * does not read the tag or the length field from the input stream, so + * the caller must supply the number of octets in this OID's encoded + * form. + * + * @param derIn The DER input stream. + * @param len The number of bytes in the encoded form. + * @param relative The relative flag. + * @throws IOException If an error occurs reading the OID. + */ + public OID(InputStream derIn, int len, boolean relative) throws IOException + { + der = new byte[len]; + derIn.read(der); + this.relative = relative; + try + { + components = fromDER(der, relative); + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + aioobe.printStackTrace(); + throw aioobe; + } + } + + /** + * Construct a new OID from the given DER bytes. + * + * @param encoded The DER encoded OID. + * @throws IOException If an error occurs reading the OID. + */ + public OID(byte[] encoded) throws IOException + { + this(encoded, false); + } + + /** + * Construct a new OID from the given DER bytes. + * + * @param root The root OID. + * @param encoded The encoded relative OID. + * @param relative The relative flag. + */ + public OID(byte[] encoded, boolean relative) throws IOException + { + der = (byte[]) encoded.clone(); + this.relative = relative; + try + { + components = fromDER(der, relative); + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + aioobe.printStackTrace(); + throw aioobe; + } + } + + /** + * Our private constructor. + */ + private OID() + { + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the numeric IDs of this OID. The value returned is copied to + * prevent modification. + * + * @return The IDs in a new integer array. + */ + public int[] getIDs() + { + return (int[]) components.clone(); + } + + /** + * Get the DER encoding of this OID, minus the tag and length fields. + * + * @return The DER bytes. + */ + public byte[] getDER() + { + if (der == null) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int i = 0; + if (!relative) + { + int b = components[i++] * 40 + (components.length > 1 + ? components[i++] : 0); + encodeSubID(bout, b); + } + for ( ; i < components.length; i++) + encodeSubID(bout, components[i]); + der = bout.toByteArray(); + } + return (byte[]) der.clone(); + } + + /** + * Get the parent OID of this OID. That is, if this OID is "1.2.3.4", + * then the parent OID will be "1.2.3". If this OID is a top-level + * OID, this method returns null. + * + * @return The parent OID, or null. + */ + public OID getParent() + { + if (components.length == 1) + return null; + int[] parent = new int[components.length - 1]; + System.arraycopy(components, 0, parent, 0, parent.length); + return new OID(parent); + } + + public OID getChild(int id) + { + int[] child = new int[components.length + 1]; + System.arraycopy(components, 0, child, 0, components.length); + child[child.length - 1] = id; + return new OID(child); + } + + /** + * Get the root OID of this OID. That is, the first two components. + * + * @return The root OID. + */ + public OID getRoot() + { + if (components.length <= 2) + return this; + int[] root = new int[2]; + root[0] = components[0]; + root[1] = components[1]; + return new OID(root); + } + + public boolean isRelative() + { + return relative; + } + + /** + * Returns a copy of this OID. + * + * @return The copy. + */ + public Object clone() + { + OID oid = new OID(); + oid.components = this.components; + oid.strRep = this.strRep; + return oid; + } + + /* Nice idea, but possibly too expensive for whatever benefit it + * provides. + + public String getShortName() + { + return OIDTable.getShortName(this); + } + + public String getLongName() + { + return OIDTable.getLongName(this); + } + + */ + + /** + * Returns the value of this OID in dotted-decimal format. + * + * @return The string representation. + */ + public String toString() + { + if (strRep != null) + return strRep; + else + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < components.length; i++) + { + buf.append((long) components[i] & 0xFFFFFFFFL); + if (i < components.length - 1) + buf.append('.'); + } + return (strRep = buf.toString()); + } + } + + /** + * Computes a hash code for this OID. + * + * @return The hash code. + */ + public int hashCode() + { + int ret = 0; + for (int i = 0; i < components.length; i++) + ret += components[i] << (i & 31); + return ret; + } + + /** + * Tests whether or not this OID equals another. + * + * @return Whether or not this OID equals the other. + */ + public boolean equals(Object o) + { + if (this == o) + return true; + return java.util.Arrays.equals(components, ((OID) o).components); + } + + /** + * Compares this OID to another. The comparison is essentially + * lexicographic, where the two OIDs are compared until their + * first difference, then that difference is returned. If one OID is + * shorter, but all elements equal between the two for the shorter + * length, then the shorter OID is lesser than the longer. + * + * @param o The object to compare. + * @return An integer less than, equal to, or greater than zero if + * this object is less than, equal to, or greater than the + * argument. + * @throws ClassCastException If <i>o</i> is not an OID. + */ + public int compareTo(Object o) + { + if (o == this) + return 0; + int[] components2 = ((OID) o).components; + int len = Math.min(components.length, components2.length); + for (int i = 0; i < len; i++) + { + if (components[i] != components2[i]) + return (components[i] < components2[i]) ? -1 : 1; + } + if (components.length == components2.length) + return 0; + return (components.length < components2.length) ? -1 : 1; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private static int[] fromDER(byte[] der, boolean relative) + throws DEREncodingException + { + // cannot be longer than this. + int[] components = new int[der.length + 1]; + int count = 0; + int i = 0; + if (!relative && i < der.length) + { + // Non-relative OIDs have the first two arcs coded as: + // + // i = first_arc * 40 + second_arc; + // + int j = (der[i] & 0xFF); + components[count++] = j / 40; + components[count++] = j % 40; + i++; + } + while (i < der.length) + { + int j = 0; + do + { + j = der[i++] & 0xFF; + components[count] <<= 7; + components[count] |= j & 0x7F; + if (i >= der.length && (j & 0x80) != 0) + throw new DEREncodingException("malformed OID"); + } + while ((j & 0x80) != 0); + count++; + } + if (count == components.length) + return components; + int[] ret = new int[count]; + System.arraycopy(components, 0, ret, 0, count); + return ret; + } + + private static int[] fromString(String strRep) throws NumberFormatException + { + if (strRep.startsWith("OID.") || strRep.startsWith("oid.")) + strRep = strRep.substring(4); + StringTokenizer tok = new StringTokenizer(strRep, "."); + if (tok.countTokens() == 0) + throw new IllegalArgumentException(); + int[] components = new int[tok.countTokens()]; + int i = 0; + while (tok.hasMoreTokens()) + { + components[i++] = Integer.parseInt(tok.nextToken()); + } + return components; + } + + private static void encodeSubID(ByteArrayOutputStream out, int id) + { + if (id < 128) + { + out.write(id); + } + else if (id < 16384) + { + out.write((id >>> 7) | 0x80); + out.write(id & 0x7F); + } + else if (id < 2097152) + { + out.write((id >>> 14) | 0x80); + out.write(((id >>> 7) | 0x80) & 0xFF); + out.write(id & 0x7F); + } + else if (id < 268435456) + { + out.write( (id >>> 21) | 0x80); + out.write(((id >>> 14) | 0x80) & 0xFF); + out.write(((id >>> 7) | 0x80) & 0xFF); + out.write(id & 0x7F); + } + } +} diff --git a/libjava/gnu/java/security/der/BitString.java b/libjava/gnu/java/security/der/BitString.java new file mode 100644 index 0000000..c4c2d9a --- /dev/null +++ b/libjava/gnu/java/security/der/BitString.java @@ -0,0 +1,317 @@ +/* BitString.java -- Java representation of the BIT STRING type. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.der; + +import java.math.BigInteger; +import java.util.Arrays; + +/** + * Immutable representation of a bit string, which is equivalent to a + * byte array except some number of the rightmost bits are ignored. For + * example, this could be the bit string: + * + * <pre> 00010101 11101101 11010xxx</pre> + * + * <p>Where the "xxx" represents three bits that should be ignored, and + * can have any value. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class BitString implements Cloneable, Comparable, java.io.Serializable +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The bits themselves. */ + private final byte[] bytes; + + /** + * The exportable byte array. This array has the ignored bits + * removed. + */ + private transient byte[] externBytes; + + /** The number of bits ignored at the end of the byte array. */ + private final int ignoredBits; + + /** This bit string as a boolean array. */ + private transient boolean[] boolVal; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new bit string, shifting the given byte array if needed. + * + * @param bytes The byte array holding the bit string. + * @param ignoredBits The number of bits to ignore. + * @param doShift Pass true in this parameter if the byte array has + * not yet been shifted left by <i>ignoredBits</i>. + * @throws IllegalArgumentException If <i>ignoredBits</i> is negative + * or greater than 7. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes, int ignoredBits, boolean doShift) + { + this(bytes, 0, bytes.length, ignoredBits, doShift); + } + + /** + * Create a new bit string, shifting the given byte array if needed. + * + * @param bytes The byte array holding the bit string. + * @param offset The offset where the meaningful bytes begin. + * @param length The number of meaningful bytes. + * @param ignoredBits The number of bits to ignore. + * @param doShift Pass true in this parameter if the byte array has + * not yet been shifted left by <i>ignoredBits</i>. + * @throws IllegalArgumentException If <i>ignoredBits</i> is negative + * or greater than 7. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes, int offset, int length, + int ignoredBits, boolean doShift) + { + if (ignoredBits < 0 || ignoredBits > 7) + throw new IllegalArgumentException(); + if (bytes == null) + throw new NullPointerException(); + if (doShift && ignoredBits > 0) + { + this.externBytes = new byte[length]; + System.arraycopy(bytes, offset, externBytes, 0, length); + this.bytes = new BigInteger(externBytes).shiftLeft(ignoredBits) + .toByteArray(); + } + else + { + this.bytes = new byte[length]; + System.arraycopy(bytes, offset, this.bytes, 0, length); + } + this.ignoredBits = ignoredBits; + } + + /** + * Create a new bit string. + * + * @param bytes The byte array holding the bit string. + * @param offset The offset where the meaningful bytes begin. + * @param length The number of meaningful bytes. + * @param ignoredBits The number of bits to ignore. + * @throws IllegalArgumentException If <i>ignoredBits</i> is negative + * or greater than 7. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes, int offset, int length, int ignoredBits) + { + this(bytes, offset, length, ignoredBits, false); + } + + /** + * Create a new bit string. + * + * @param bytes The byte array holding the bit string. + * @param ignoredBits The number of bits to ignore. + * @throws IllegalArgumentException If <i>ignoredBits</i> is negative + * or greater than 7. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes, int ignoredBits) + { + this(bytes, 0, bytes.length, ignoredBits, false); + } + + /** + * Create a new bit string. + * + * @param bytes The byte array holding the bit string. + * @param offset The offset where the meaningful bytes begin. + * @param length The number of meaningful bytes. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes, int offset, int length) + { + this(bytes, offset, length, 0, false); + } + + /** + * Create a new bit string. + * + * @param bytes The byte array holding the bit string. + * @throws NullPointerException If <i>bytes</i> is null. + */ + public BitString(byte[] bytes) + { + this(bytes, 0, bytes.length, 0, false); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return this bit string as a byte array, with the ignored bits + * trimmed off. The byte array is cloned every time this method is + * called to prevent modification. + * + * @return The trimmed byte array. + */ + public byte[] toByteArray() + { + if (ignoredBits == 0) + return (byte[]) bytes.clone(); + if (externBytes == null) + externBytes = new BigInteger(bytes).shiftRight(ignoredBits).toByteArray(); + return (byte[]) externBytes.clone(); + } + + /** + * Returns this bit string as a byte array, with the ignored bits + * present. The byte array is cloned every time this method is + * called to prevent modification. + * + * @return The byte array. + */ + public byte[] getShiftedByteArray() + { + return (byte[]) bytes.clone(); + } + + /** + * Returns the number of ignored bits. + * + * @return The number of ignored bits. + */ + public int getIgnoredBits() + { + return ignoredBits; + } + + /** + * Returns the size, in bits, of this bit string. + * + * @return The size of this bit string. + */ + public int size() + { + return (bytes.length << 3) - ignoredBits; + } + + /** + * Return this bit string as a boolean array. The value returned is of + * size {@link #size()}, and each <code>true</code> value + * corresponding to each "1" in this bit string. The boolean array is + * cloned before it is returned. + * + * @return The boolean array. + */ + public boolean[] toBooleanArray() + { + if (boolVal == null) + { + boolVal = new boolean[size()]; + for (int i = 0, j = 7, k = 0; i < boolVal.length; i++) + { + boolVal[i] = (bytes[k] & 1 << j--) != 0; + if (j < 0) + { + j = 7; + k++; + } + } + } + return (boolean[]) boolVal.clone(); + } + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException cce) + { + throw new InternalError(cce.getMessage()); + } + } + + public int compareTo(Object o) + { + BitString that = (BitString) o; + if (this.equals(that)) + return 0; + if (this.bytes.length != that.bytes.length) + return (this.bytes.length < that.bytes.length) ? -1 : 1; + if (this.ignoredBits != that.ignoredBits) + return (this.ignoredBits < that.ignoredBits) ? -1 : 1; + for (int i = 0; i < this.bytes.length; i++) + if (this.bytes[i] != that.bytes[i]) + return (this.bytes[i] < that.bytes[i]) ? -1 : 1; + return 0; // not reached. + } + + public boolean equals(Object o) + { + if (this == o) + return true; + BitString that = (BitString) o; + // True for cloned instances. + if (this.bytes == that.bytes && this.ignoredBits == that.ignoredBits) + return true; + if (this.ignoredBits == that.ignoredBits) + return Arrays.equals(this.bytes, that.bytes); + return false; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + for (int i = 0, j = 7, k = 0; i < size(); i++) + { + sb.append((bytes[k] & 1 << j) != 0 ? "1" : "0"); + j--; + if (j < 0) + { + j = 7; + k++; + } + } + return sb.toString(); + } +} diff --git a/libjava/gnu/java/security/der/DER.java b/libjava/gnu/java/security/der/DER.java new file mode 100644 index 0000000..9435366 --- /dev/null +++ b/libjava/gnu/java/security/der/DER.java @@ -0,0 +1,90 @@ +/* DER.java -- Basic constants in DER sequences. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.der; + +/** + * The set of tags for DER types. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public interface DER +{ + + // Constants. + // ------------------------------------------------------------------------ + + public static final int UNIVERSAL = 0x00; + public static final int APPLICATION = 0x40; + public static final int CONTEXT = 0x80; + public static final int PRIVATE = 0xC0; + + public static final int CONSTRUCTED = 0x20; + + public static final int ANY = 0x00; + public static final int BOOLEAN = 0x01; + public static final int INTEGER = 0x02; + public static final int BIT_STRING = 0x03; + public static final int OCTET_STRING = 0x04; + public static final int NULL = 0x05; + public static final int OBJECT_IDENTIFIER = 0x06; + public static final int REAL = 0x09; + public static final int ENUMERATED = 0x0a; + public static final int RELATIVE_OID = 0x0d; + + public static final int SEQUENCE = 0x10; + public static final int SET = 0x11; + + public static final Object CONSTRUCTED_VALUE = new Object(); + + public static final int NUMERIC_STRING = 0x12; + public static final int PRINTABLE_STRING = 0x13; + public static final int T61_STRING = 0x14; + public static final int VIDEOTEX_STRING = 0x15; + public static final int IA5_STRING = 0x16; + public static final int GRAPHIC_STRING = 0x19; + public static final int ISO646_STRING = 0x1A; + public static final int GENERAL_STRING = 0x1B; + + public static final int UTF8_STRING = 0x0C; + public static final int UNIVERSAL_STRING = 0x1C; + public static final int BMP_STRING = 0x1E; + + public static final int UTC_TIME = 0x17; + public static final int GENERALIZED_TIME = 0x18; +} diff --git a/libjava/gnu/java/security/der/DEREncodingException.java b/libjava/gnu/java/security/der/DEREncodingException.java index eea40b8..385fbac 100644 --- a/libjava/gnu/java/security/der/DEREncodingException.java +++ b/libjava/gnu/java/security/der/DEREncodingException.java @@ -1,5 +1,5 @@ /* DEREncodingException.java --- DER Encoding Exception - Copyright (C) 1999 Free Software Foundation, Inc. + Copyright (C) 1999,2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,9 +38,10 @@ exception statement from your version. */ package gnu.java.security.der; -public class DEREncodingException extends Exception -{ +import java.io.IOException; +public class DEREncodingException extends IOException +{ public DEREncodingException() { super (); diff --git a/libjava/gnu/java/security/der/DERReader.java b/libjava/gnu/java/security/der/DERReader.java new file mode 100644 index 0000000..3020b8b --- /dev/null +++ b/libjava/gnu/java/security/der/DERReader.java @@ -0,0 +1,382 @@ +/* DERReader.java -- parses ASN.1 DER sequences + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.der; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.InputStream; +import java.io.IOException; + +import java.math.BigInteger; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import gnu.java.security.OID; + +/** + * This class decodes DER sequences into Java objects. The methods of + * this class do not have knowledge of higher-levels of structure in the + * DER stream -- such as ASN.1 constructions -- and it is therefore up + * to the calling application to determine if the data are structured + * properly by inspecting the {@link DERValue} that is returned. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class DERReader implements DER +{ + + // Fields. + // ------------------------------------------------------------------------ + + protected InputStream in; + + protected final ByteArrayOutputStream encBuf; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new DER reader from a byte array. + * + * @param in The encoded bytes. + */ + public DERReader(byte[] in) + { + this(new ByteArrayInputStream(in)); + } + + /** + * Create a new DER readed from an input stream. + * + * @param in The encoded bytes. + */ + public DERReader(InputStream in) + { + if (!in.markSupported()) + this.in = new BufferedInputStream(in, 16384); + else + this.in = in; + encBuf = new ByteArrayOutputStream(2048); + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Convenience method for reading a single primitive value from the + * given byte array. + * + * @param encoded The encoded bytes. + * @throws IOException If the bytes do not represent an encoded + * object. + */ + public static DERValue read(byte[] encoded) throws IOException + { + return new DERReader(encoded).read(); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Decode a single value from the input stream, returning it in a new + * {@link DERValue}. By "single value" we mean any single type in its + * entirety -- including constructed types such as SEQUENCE and all + * the values they contain. Usually it is sufficient to call this + * method once to parse and return the top-level structure, then to + * inspect the returned value for the proper contents. + * + * @return The parsed DER structure. + * @throws IOException If an error occurs reading from the input + * stream. + * @throws DEREncodingException If the input does not represent a + * valid DER stream. + */ + public DERValue read() throws IOException + { + int tag = in.read(); + if (tag == -1) + throw new EOFException(); + encBuf.write(tag); + int len = readLength(); + DERValue value = null; + if ((tag & CONSTRUCTED) == CONSTRUCTED) + { + in.mark(2048); + byte[] encoded = new byte[len]; + in.read(encoded); + encBuf.write(encoded); + value = new DERValue(tag, len, CONSTRUCTED_VALUE, encBuf.toByteArray()); + in.reset(); + encBuf.reset(); + return value; + } + switch (tag & 0xC0) + { + case UNIVERSAL: + value = new DERValue(tag, len, readUniversal(tag, len), + encBuf.toByteArray()); + encBuf.reset(); + break; + case CONTEXT: + byte[] encoded = new byte[len]; + in.read(encoded); + encBuf.write(encoded); + value = new DERValue(tag, len, encoded, encBuf.toByteArray()); + encBuf.reset(); + break; + case APPLICATION: + // This should not be reached, since (I think) APPLICATION is + // always constructed. + throw new DEREncodingException("non-constructed APPLICATION data"); + default: + throw new DEREncodingException("PRIVATE class not supported"); + } + return value; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private Object readUniversal(int tag, int len) throws IOException + { + byte[] value = new byte[len]; + in.read(value); + encBuf.write(value); + switch (tag & 0x1F) + { + case BOOLEAN: + if (value.length != 1) + throw new DEREncodingException(); + return Boolean.valueOf(value[0] != 0); + case NULL: + if (len != 0) + throw new DEREncodingException(); + return null; + case INTEGER: + case ENUMERATED: + return new BigInteger(value); + case BIT_STRING: + byte[] bits = new byte[len - 1]; + System.arraycopy(value, 1, bits, 0, bits.length); + return new BitString(bits, value[0] & 0xFF); + case OCTET_STRING: + return value; + case NUMERIC_STRING: + case PRINTABLE_STRING: + case T61_STRING: + case VIDEOTEX_STRING: + case IA5_STRING: + case GRAPHIC_STRING: + case ISO646_STRING: + case GENERAL_STRING: + case UNIVERSAL_STRING: + case BMP_STRING: + case UTF8_STRING: + return makeString(tag, value); + case UTC_TIME: + case GENERALIZED_TIME: + return makeTime(tag, value); + case OBJECT_IDENTIFIER: + return new OID(value); + case RELATIVE_OID: + return new OID(value, true); + default: + throw new DEREncodingException("unknown tag " + tag); + } + } + + private int readLength() throws IOException + { + int i = in.read(); + if (i == -1) + throw new EOFException(); + encBuf.write(i); + if ((i & ~0x7F) == 0) + { + return i; + } + else if (i < 0xFF) + { + byte[] octets = new byte[i & 0x7F]; + in.read(octets); + encBuf.write(octets); + return new BigInteger(1, octets).intValue(); + } + throw new DEREncodingException(); + } + + private String makeString(int tag, byte[] value) + throws IOException + { + Charset charset = null; + switch (tag & 0x1F) + { + case NUMERIC_STRING: + case PRINTABLE_STRING: + case T61_STRING: + case VIDEOTEX_STRING: + case IA5_STRING: + case GRAPHIC_STRING: + case ISO646_STRING: + case GENERAL_STRING: + charset = Charset.forName("ISO-8859-1"); + break; + case UNIVERSAL_STRING: + // XXX The docs say UniversalString is encoded in four bytes + // per character, but Java has no support (yet) for UTF-32. + //return new String(buf, "UTF-32"); + case BMP_STRING: + charset = Charset.forName("UTF-16BE"); + break; + case UTF8_STRING: + charset = Charset.forName("UTF-8"); + break; + default: + throw new DEREncodingException("unknown string tag"); + } + if (charset == null) + throw new DEREncodingException("no decoder"); + CharsetDecoder decoder = charset.newDecoder(); + CharBuffer result = decoder.decode(ByteBuffer.wrap(value)); + char[] buf = new char[result.remaining()]; + result.get(buf); + return new String(buf); + } + + private Date makeTime(int tag, byte[] value) throws IOException + { + Calendar calendar = Calendar.getInstance(); + String str = makeString(PRINTABLE_STRING, value); + + // Classpath's SimpleDateFormat does not work for parsing these + // types of times, so we do this by hand. + String date = str; + String tz = ""; + if (str.indexOf("+") > 0) + { + date = str.substring(0, str.indexOf("+")); + tz = str.substring(str.indexOf("+")); + } + else if (str.indexOf("-") > 0) + { + date = str.substring(0, str.indexOf("-")); + tz = str.substring(str.indexOf("-")); + } + else if (str.endsWith("Z")) + { + date = str.substring(0, str.length()-2); + tz = "Z"; + } + if (!tz.equals("Z") && tz.length() > 0) + calendar.setTimeZone(TimeZone.getTimeZone(tz)); + else + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + if ((tag & 0x1F) == UTC_TIME) + { + if (date.length() < 10) // must be at least 10 chars long + throw new DEREncodingException("cannot parse date"); + // UTCTime is of the form "yyMMddHHmm[ss](Z|(+|-)hhmm)" + try + { + int year = Integer.parseInt(str.substring(0, 2)); + if (year < 50) + year += 2000; + else + year += 1900; + calendar.set(year, + Integer.parseInt(str.substring( 2, 4))-1, // month + Integer.parseInt(str.substring( 4, 6)), // day + Integer.parseInt(str.substring( 6, 8)), // hour + Integer.parseInt(str.substring( 8, 10))); // minute + if (date.length() == 12); + calendar.set(calendar.SECOND, + Integer.parseInt(str.substring(10, 12))); + } + catch (NumberFormatException nfe) + { + throw new DEREncodingException("cannot parse date"); + } + } + else + { + if (date.length() < 10) // must be at least 10 chars long + throw new DEREncodingException("cannot parse date"); + // GeneralTime is of the form "yyyyMMddHH[mm[ss[(.|,)SSSS]]]" + // followed by "Z" or "(+|-)hh[mm]" + try + { + calendar.set( + Integer.parseInt(date.substring(0, 4)), // year + Integer.parseInt(date.substring(4, 6))-1, // month + Integer.parseInt(date.substring(6, 8)), // day + Integer.parseInt(date.substring(8, 10)), 0); // hour, min + switch (date.length()) + { + case 19: + case 18: + case 17: + case 16: + calendar.set(calendar.MILLISECOND, + Integer.parseInt(date.substring(15))); + case 14: + calendar.set(calendar.SECOND, + Integer.parseInt(date.substring(12, 14))); + case 12: + calendar.set(calendar.MINUTE, + Integer.parseInt(date.substring(10, 12))); + } + } + catch (NumberFormatException nfe) + { + throw new DEREncodingException("cannot parse date"); + } + } + return calendar.getTime(); + } +} diff --git a/libjava/gnu/java/security/der/DERValue.java b/libjava/gnu/java/security/der/DERValue.java new file mode 100644 index 0000000..dd8afc4 --- /dev/null +++ b/libjava/gnu/java/security/der/DERValue.java @@ -0,0 +1,168 @@ +/* DERValue.java -- a value read or written to a DER encoding. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.der; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class DERValue implements DER +{ + + // Fields. + // ------------------------------------------------------------------------ + + private final int tagClass; + + private final boolean constructed; + + private final int tag; + + private int length; + + private final Object value; + + private byte[] encoded; + + // Constructor. + // ------------------------------------------------------------------------ + + public DERValue(int tag, int length, Object value, byte[] encoded) + { + tagClass = tag & 0xC0; + this.tag = tag & 0x1F; + constructed = (tag & CONSTRUCTED) == CONSTRUCTED; + this.length = length; + this.value = value; + if (encoded != null) + this.encoded = (byte[]) encoded.clone(); + } + + public DERValue(int tag, Object value) + { + this(tag, 0, value, null); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public int getExternalTag() + { + return tagClass | tag | (constructed ? 0x20 : 0x00); + } + + public int getTag() + { + return tag; + } + + public int getTagClass() + { + return tagClass; + } + + public boolean isConstructed() + { + return constructed; + } + + public int getLength() + { + if (encoded == null) + { + try + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + length = DERWriter.write(out, this); + encoded = out.toByteArray(); + } + catch (IOException ioe) + { + encoded = new byte[0]; + } + } + return length; + } + + public Object getValue() + { + return value; + } + + public byte[] getEncoded() + { + if (encoded == null) + { + try + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + length = DERWriter.write(out, this); + encoded = out.toByteArray(); + } + catch (IOException ioe) + { + encoded = new byte[0]; + } + } + return (byte[]) encoded.clone(); + } + + public int getEncodedLength() + { + if (encoded == null) + { + try + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + length = DERWriter.write(out, this); + encoded = out.toByteArray(); + } + catch (IOException ioe) + { + encoded = new byte[0]; + } + } + return encoded.length; + } + + public String toString() + { + return "DERValue [ tag=" + tag + ", class=" + tagClass + ", constructed=" + + constructed + ", value=" + value + " ]"; + } +} diff --git a/libjava/gnu/java/security/der/DERWriter.java b/libjava/gnu/java/security/der/DERWriter.java new file mode 100644 index 0000000..ec3e3df --- /dev/null +++ b/libjava/gnu/java/security/der/DERWriter.java @@ -0,0 +1,324 @@ +/* DERWriter.java -- write Java types in DER format. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.der; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.math.BigInteger; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +import java.text.SimpleDateFormat; + +import java.util.BitSet; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TimeZone; + +import gnu.java.security.OID; + +/** + * Methods that allow various Java types to be written as a DER + * (Distinguished Encoding Rules) stream to the specified output stream. + * DER is used to encode ASN.1 constructions, but this class provides no + * methods for interacting with ASN.1. Rather, callers should construct + * their output objects properly for whatever ASN.1 construct is being + * output. + * + * <p>This class only defines static methods; there are no instance + * variables needed. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class DERWriter implements DER +{ + + // Constructors. + // ------------------------------------------------------------------------ + + /** This class only has static methods. */ + private DERWriter() + { + } + + // Class methods. + // ------------------------------------------------------------------------ + + public static int write(OutputStream out, DERValue object) + throws IOException + { + out.write(object.getExternalTag()); + Object value = object.getValue(); + if (value == null) + { + writeLength(out, 0); + return 0; + } + if (value instanceof Boolean) + return writeBoolean(out, (Boolean) value); + else if (value instanceof BigInteger) + return writeInteger(out, (BigInteger) value); + else if (value instanceof Date) + return writeDate(out, object.getExternalTag(), (Date) value); + else if (value instanceof String) + return writeString(out, object.getExternalTag(), (String) value); + else if (value instanceof List) + return writeSequence(out, (List) value); + else if (value instanceof Set) + return writeSet(out, (Set) value); + else if (value instanceof BitString) + return writeBitString(out, (BitString) value); + else if (value instanceof OID) + return writeOID(out, (OID) value); + else if (value instanceof byte[]) + { + writeLength(out, ((byte[]) value).length); + out.write((byte[]) value); + return ((byte[]) value).length; + } + else if (value instanceof DERValue) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + write(bout, (DERValue) value); + byte[] buf = bout.toByteArray(); + writeLength(out, buf.length); + out.write(buf); + return buf.length; + } + else + throw new DEREncodingException("cannot encode " + value.getClass().getName()); + } + + public static int definiteEncodingSize(int length) + { + if (length < 128) + return 1; + else if (length < 256) + return 2; + else if (length < 65536) + return 3; + else if (length < 16777216) + return 4; + else + return 5; + } + + // Own methods. + // ------------------------------------------------------------------------ + + /** + * Write a BOOLEAN type to the given output stream. + * + * @param out The sink output stream. + * @param b The boolean value to write. + */ + private static int writeBoolean(OutputStream out, Boolean b) + throws IOException + { + writeLength(out, 1); + if (b.booleanValue()) + out.write(0xFF); + else + out.write(0); + return 1; + } + + /** + * Write an INTEGER type to the given output stream. + * + * @param out The sink output stream. + * @param integer The integer to write. + */ + private static int writeInteger(OutputStream out, BigInteger integer) + throws IOException + { + byte[] bytes = integer.toByteArray(); + writeLength(out, bytes.length); + out.write(bytes); + return bytes.length; + } + + private static int writeSequence(OutputStream out, List sequence) + throws IOException + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + for (Iterator i = sequence.iterator(); i.hasNext(); ) + { + write(bout, (DERValue) i.next()); + } + byte[] buf = bout.toByteArray(); + writeLength(out, buf.length); + out.write(buf); + return buf.length; + } + + private static int writeSet(OutputStream out, Set set) + throws IOException + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + for (Iterator i = set.iterator(); i.hasNext(); ) + { + write(bout, (DERValue) i.next()); + } + byte[] buf = bout.toByteArray(); + writeLength(out, buf.length); + out.write(buf); + return buf.length; + } + + private static int writeOID(OutputStream out, OID oid) + throws IOException + { + byte[] der = oid.getDER(); + writeLength(out, der.length); + out.write(der); + return der.length; + } + + private static int writeBitString(OutputStream out, BitString bs) + throws IOException + { + byte[] buf = bs.getShiftedByteArray(); + out.write(buf.length + 1); + out.write(bs.getIgnoredBits()); + out.write(buf); + return buf.length; + } + + private static int writeString(OutputStream out, int tag, String str) + throws IOException + { + Charset charset = null; + byte[] b = null; + switch (tag & 0x1F) + { + case NUMERIC_STRING: + case PRINTABLE_STRING: + case T61_STRING: + case VIDEOTEX_STRING: + case IA5_STRING: + case GRAPHIC_STRING: + case ISO646_STRING: + case GENERAL_STRING: + charset = Charset.forName("ISO-8859-1"); + break; + case UNIVERSAL_STRING: + case BMP_STRING: + charset = Charset.forName("UTF-16BE"); + break; + case UTF8_STRING: + default: + charset = Charset.forName("UTF-8"); + break; + } + if (charset == null) + throw new DEREncodingException("no charset"); + CharsetEncoder encoder = charset.newEncoder(); + ByteBuffer result = encoder.encode(CharBuffer.wrap(str)); + if (result.hasArray()) + { + b = result.array(); + } + else + { + b = new byte[result.remaining()]; + result.get(b); + } + writeLength(out, b.length); + out.write(b); + return b.length; + } + + private static int writeDate(OutputStream out, int tag, Date date) + throws IOException + { + SimpleDateFormat sdf = null; + if ((tag & 0x1F) == UTC_TIME) + sdf = new SimpleDateFormat("yyMMddHHmmss'Z'"); + else + sdf = new SimpleDateFormat("yyyyMMddHHmmss'.'SSS'Z'"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + byte[] b = sdf.format(date).getBytes("ISO-8859-1"); + writeLength(out, b.length); + out.write(b); + return b.length; + } + + // Package method. + // ------------------------------------------------------------------------ + + static void writeLength(OutputStream out, int len) throws IOException + { + if (len < 128) + out.write(len); + else if (len < 256) + { + out.write(0x81); + out.write(len); + } + else if (len < 65536) + { + out.write(0x82); + out.write(len >> 8); + out.write(len); + } + else if (len < 16777216) + { + out.write(0x83); + out.write(len >> 16); + out.write(len >> 8); + out.write(len); + } + else + { + out.write(0x84); + out.write(len >> 24); + out.write(len >> 16); + out.write(len >> 8); + out.write(len); + } + } +} diff --git a/libjava/gnu/java/security/provider/DERReader.java b/libjava/gnu/java/security/provider/DERReader.java deleted file mode 100644 index 0f6e492..0000000 --- a/libjava/gnu/java/security/provider/DERReader.java +++ /dev/null @@ -1,152 +0,0 @@ -/* DERReader.java - Copyright (C) 1999 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath 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; either version 2, or (at your option) -any later version. - -GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -02111-1307 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - - -package gnu.java.security.provider; - -import java.math.BigInteger; -import gnu.java.security.der.DEREncodingException; - -public class DERReader -{ - byte source[]; - int pos; - - static final int UNIVERSAL = 1; - static final int APPLICATION = 2; - static final int CONTEXT_SPECIFIC = 3; - static final int PRIVATE = 4; - - - public DERReader() - { - source = null; - pos = 0; - } - - public DERReader( byte source[] ) - { - init( source ); - } - - public void init( String source ) - { - init( source.getBytes() ); - } - - public void init( byte source[] ) - { - this.source = source; - pos = 0; - } - - public BigInteger getBigInteger() throws DEREncodingException - { - return new BigInteger( getPrimitive() ); - } - - //Reads Primitive, definite-length method - private byte[] getPrimitive() throws DEREncodingException - { - int tmp = pos; - - //Read Identifier - byte identifier = source[tmp++]; - if( (0x20 & identifier) != 0) - throw new DEREncodingException(); - int type = translateLeadIdentifierByte(identifier); - //System.out.println("Type: " + type); - - //get tag - int tag = (0x1f & identifier); - //if( tag == 0x1f) - // tag = getIdentifier(tmp); - //System.out.println("Tag: " + tag); - - //get length - byte len = source[tmp]; //may be length of length parameter - long length = 0x7f & len; - int i; - if( (0x80 & len) != 0 ) { - //System.out.println("Extra Long Length"); - len &= 0x7f; - //System.out.println("Length of Length: " + len); - //get length here - length = 0; - for( i = 0; i < len; i++ ) { - tmp++; - length <<= 8; - length += (source[tmp] < 0 ) ? - (256 + source[tmp]) : - source[tmp]; - //System.out.println("Length of Length: " + length); - } - tmp++; - } else - tmp++; - - /*System.out.println("Position: " + tmp); - System.out.println("Length: " + length); - for( i = 0; i < 10; i++) - System.out.print(source[tmp + i] + " "); - System.out.println();*/ - - byte tmpb[] = new byte[ (int)length ]; - System.arraycopy( source, tmp, tmpb, 0, (int)length); - pos = (int)(tmp + length); - return tmpb; - } - - private int translateLeadIdentifierByte(byte b) - { - if( (0x3f & b ) == b) - return UNIVERSAL; - else if( (0x7f & b ) == b) - return APPLICATION; - else if( (0xbf & b ) == b) - return CONTEXT_SPECIFIC; - else - return PRIVATE; - } - - private int getIdentifier(int tpos) - { - while( (0x80 & source[tpos]) != 0) - tpos++; - return tpos; - } -} diff --git a/libjava/gnu/java/security/provider/DERWriter.java b/libjava/gnu/java/security/provider/DERWriter.java deleted file mode 100644 index 4add119..0000000 --- a/libjava/gnu/java/security/provider/DERWriter.java +++ /dev/null @@ -1,142 +0,0 @@ -/* DERWriter.java - Copyright (C) 1999 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath 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; either version 2, or (at your option) -any later version. - -GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -02111-1307 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - - -package gnu.java.security.provider; - -import java.math.BigInteger; - -public class DERWriter -{ - static final int UNIVERSAL = 1; - static final int APPLICATION = 2; - static final int CONTEXT_SPECIFIC = 3; - static final int PRIVATE = 4; - - public DERWriter() - {} - - public byte[] writeBigInteger( BigInteger i) - { - return writePrimitive( 0x02, UNIVERSAL, (int)Math.ceil((double)i.bitLength() / 8), i.toByteArray() ); - } - - private byte[] writePrimitive( int identifier, int identifierencoding, - int length, byte contents[]) - { - return joinarrays( generateIdentifier( identifier, identifierencoding ), generateLength( length ), contents); - } - - public byte[] joinarrays( byte a[], byte b[]) - { - byte d[] = new byte[ a.length + b.length]; - System.arraycopy( a, 0, d, 0, a.length); - System.arraycopy( b, 0, d, a.length, b.length); - return d; - } - - public byte[] joinarrays( byte a[], byte b[], byte c[]) - { - byte d[] = new byte[ a.length + b.length + c.length]; - System.arraycopy( a, 0, d, 0, a.length); - System.arraycopy( b, 0, d, a.length, b.length); - System.arraycopy( c, 0, d, a.length + b.length, c.length); - return d; - } - - private byte[] generateIdentifier(int identifier, - int identifierencoding) - { - byte b[]; - if( identifier > 31 ) { - int count = (int)(Math.log( identifier ) / Math.log( 256 )); - b = new byte[ count + 1 ]; - b[0] = (byte)(translateLeadIdentifierByte(identifierencoding) - | 0x1f); - int i; - for( i = 1; i < (count + 1); i++) { - b[i] = (byte)(0x7f & ( identifier >> (7 * (count - i)) )); - b[i] |= 0x80; - } - b[i - 1] ^= 0x80; - //System.out.println("Identifier1: " + b[0]); - return b; - } else { - b = new byte[1]; - b[0] = (byte)((translateLeadIdentifierByte(identifierencoding) - | (byte)( identifier & 0x1f )) & 0xdf); - //System.out.println("Identifier2: " + b[0]); - return b; - } - } - - private byte translateLeadIdentifierByte(int b) - { - if( b == UNIVERSAL) - return (byte)0x3f; - else if( b == APPLICATION) - return (byte)0x7f; - else if( b == CONTEXT_SPECIFIC) - return (byte)0xbf; - else - return (byte)0xC0; - } - - private byte[] generateLength( int length ) - { - byte b[]; - if( length > 127 ) { - int count = (int)Math.ceil(Math.log( length ) / Math.log( 256 )); - //System.out.println("Length byte count: " + count); - b = new byte[ count + 1 ]; - b[0] = (byte)((count & 0x7f) | 0x80); - for( int i = 1; i < (count + 1); i++) { - b[i] = (byte)( length >>> (8 * ( count - i) )); - //System.out.println("Length1 byte1: " + (length >>> (8 * ( count - i) ))); - //System.out.println("Length1 byte2: " + b[i]); - } - - //System.out.println("Length1: " + length); - return b; - } else { - b = new byte[1]; - b[0] = (byte)( length & 0x7f ); - //System.out.println("Length2: " + length); - return b; - } - } -} diff --git a/libjava/gnu/java/security/provider/DSAKeyFactory.java b/libjava/gnu/java/security/provider/DSAKeyFactory.java new file mode 100644 index 0000000..3414f42 --- /dev/null +++ b/libjava/gnu/java/security/provider/DSAKeyFactory.java @@ -0,0 +1,134 @@ +/* DSAKeyFactory.java -- DSA key factory. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.provider; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * DSA key factory. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class DSAKeyFactory extends KeyFactorySpi +{ + + // Constructor. + // ------------------------------------------------------------------------ + + public DSAKeyFactory() + { + super(); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + if (!(keySpec instanceof DSAPrivateKeySpec)) + throw new InvalidKeySpecException(); + return new GnuDSAPrivateKey( + ((DSAPrivateKeySpec) keySpec).getX(), + ((DSAPrivateKeySpec) keySpec).getP(), + ((DSAPrivateKeySpec) keySpec).getQ(), + ((DSAPrivateKeySpec) keySpec).getG()); + } + + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + if (!(keySpec instanceof DSAPublicKeySpec)) + throw new InvalidKeySpecException(); + return new GnuDSAPublicKey( + ((DSAPublicKeySpec) keySpec).getY(), + ((DSAPublicKeySpec) keySpec).getP(), + ((DSAPublicKeySpec) keySpec).getQ(), + ((DSAPublicKeySpec) keySpec).getG()); + } + + protected KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if ((key instanceof DSAPublicKey) && + keySpec.isAssignableFrom(DSAPublicKeySpec.class)) + { + return new DSAPublicKeySpec(((DSAPublicKey) key).getY(), + ((DSAPublicKey) key).getParams().getP(), + ((DSAPublicKey) key).getParams().getQ(), + ((DSAPublicKey) key).getParams().getG()); + } + if ((key instanceof DSAPrivateKey) && + keySpec.isAssignableFrom(DSAPrivateKeySpec.class)) + { + return new DSAPrivateKeySpec(((DSAPrivateKey) key).getX(), + ((DSAPrivateKey) key).getParams().getP(), + ((DSAPrivateKey) key).getParams().getQ(), + ((DSAPrivateKey) key).getParams().getG()); + } + throw new InvalidKeySpecException(); + } + + protected Key engineTranslateKey(Key key) throws InvalidKeyException + { + if ((key instanceof GnuDSAPublicKey) || (key instanceof GnuDSAPrivateKey)) + return key; + if (key instanceof DSAPublicKey) + return new GnuDSAPublicKey(((DSAPublicKey) key).getY(), + ((DSAPublicKey) key).getParams().getP(), + ((DSAPublicKey) key).getParams().getQ(), + ((DSAPublicKey) key).getParams().getG()); + if (key instanceof DSAPrivateKey) + return new GnuDSAPrivateKey(((DSAPrivateKey) key).getX(), + ((DSAPrivateKey) key).getParams().getP(), + ((DSAPrivateKey) key).getParams().getQ(), + ((DSAPrivateKey) key).getParams().getG()); + throw new InvalidKeyException(); + } +} diff --git a/libjava/gnu/java/security/provider/DSAParameters.java b/libjava/gnu/java/security/provider/DSAParameters.java index a8f998b..31c0d5e 100644 --- a/libjava/gnu/java/security/provider/DSAParameters.java +++ b/libjava/gnu/java/security/provider/DSAParameters.java @@ -1,5 +1,5 @@ /* DSAParameters.java --- DSA Parameters Implementation - Copyright (C) 1999 Free Software Foundation, Inc. + Copyright (C) 1999,2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,15 +38,28 @@ exception statement from your version. */ package gnu.java.security.provider; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; + import java.math.BigInteger; + import java.security.AlgorithmParametersSpi; import java.security.InvalidAlgorithmParameterException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAParameterSpec; import java.security.spec.InvalidParameterSpecException; + +import java.util.ArrayList; +import java.util.List; import java.util.Random; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.der.DER; import gnu.java.security.der.DEREncodingException; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; import gnu.java.security.util.Prime; @@ -76,7 +89,7 @@ public void engineInit(AlgorithmParameterSpec paramSpec) DSAParameterSpec dsaParamSpec = (DSAParameterSpec)paramSpec; p = dsaParamSpec.getP(); q = dsaParamSpec.getQ(); - q = dsaParamSpec.getG(); + g = dsaParamSpec.getG(); } else throw new InvalidParameterSpecException("Only accepts DSAParameterSpec"); @@ -85,16 +98,20 @@ public void engineInit(AlgorithmParameterSpec paramSpec) public void engineInit(byte[] params) throws IOException { - DERReader reader = new DERReader( params ); - try { - - p = reader.getBigInteger(); - q = reader.getBigInteger(); - g = reader.getBigInteger(); - - } catch ( DEREncodingException DERee) { - throw new IOException("Invalid Format: Only accepts ASN.1"); - } + DERReader in = new DERReader(params); + DERValue val = in.read(); + if (val.getValue() != DER.CONSTRUCTED_VALUE) + throw new ASN1ParsingException("badly formed parameters"); + try + { + p = (BigInteger) in.read().getValue(); + q = (BigInteger) in.read().getValue(); + g = (BigInteger) in.read().getValue(); + } + catch (Exception x) + { + throw new ASN1ParsingException("badly formed parameters"); + } } public void engineInit(byte[] params, String format) @@ -117,10 +134,13 @@ public AlgorithmParameterSpec engineGetParameterSpec(Class paramSpec) public byte[] engineGetEncoded() throws IOException { - DERWriter writer = new DERWriter(); - return writer.joinarrays( writer.writeBigInteger(p), - writer.writeBigInteger(q), - writer.writeBigInteger(g) ); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ArrayList seq = new ArrayList(3); + seq.add(new DERValue(DER.INTEGER, p)); + seq.add(new DERValue(DER.INTEGER, q)); + seq.add(new DERValue(DER.INTEGER, g)); + DERWriter.write(bout, new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, seq)); + return bout.toByteArray(); } @@ -135,7 +155,7 @@ public byte[] engineGetEncoded(String format) public String engineToString() { String lineSeparator = System.getProperty("line.seperator"); - return ("q: " + q + lineSeparator + "p: " + p + lineSeparator + "g:" + g); + return ("q: " + q + " p: " + p + " g: " + g); } } diff --git a/libjava/gnu/java/security/provider/DSASignature.java b/libjava/gnu/java/security/provider/DSASignature.java index f33f048..7da1c7d 100644 --- a/libjava/gnu/java/security/provider/DSASignature.java +++ b/libjava/gnu/java/security/provider/DSASignature.java @@ -1,5 +1,5 @@ /* DSASignature.java - Copyright (C) 1999 Free Software Foundation, Inc. + Copyright (C) 1999,2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,7 +37,12 @@ exception statement from your version. */ package gnu.java.security.provider; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + import java.math.BigInteger; + import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.InvalidParameterException; @@ -52,8 +57,17 @@ import java.security.SignatureSpi; import java.security.interfaces.DSAPrivateKey; import java.security.interfaces.DSAPublicKey; import java.security.spec.AlgorithmParameterSpec; + +import java.util.ArrayList; +import java.util.List; import java.util.Random; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.der.DER; import gnu.java.security.der.DEREncodingException; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; public class DSASignature extends SignatureSpi { @@ -157,9 +171,14 @@ public class DSASignature extends SignatureSpi BigInteger s = sha.add( x.multiply( r ) ); s = s.multiply( k.modInverse(q) ).mod( q ); - DERWriter writer = new DERWriter(); - return writer.joinarrays( writer.writeBigInteger( r ), writer.writeBigInteger( s ) ); - + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ArrayList seq = new ArrayList(2); + seq.set(0, new DERValue(DER.INTEGER, r)); + seq.set(1, new DERValue(DER.INTEGER, s)); + DERWriter.write(bout, new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, seq)); + return bout.toByteArray(); + } catch (IOException ioe) { + throw new SignatureException(); } catch ( ArithmeticException ae ) { throw new SignatureException(); } @@ -180,9 +199,12 @@ public class DSASignature extends SignatureSpi { //Decode sigBytes from ASN.1 DER encoding try { - DERReader reader = new DERReader( sigBytes ); - BigInteger r = reader.getBigInteger(); - BigInteger s = reader.getBigInteger(); + DERReader in = new DERReader(sigBytes); + DERValue val = in.read(); + if (!val.isConstructed()) + throw new SignatureException("badly formed signature"); + BigInteger r = (BigInteger) in.read().getValue(); + BigInteger s = (BigInteger) in.read().getValue(); BigInteger g = publicKey.getParams().getG(); BigInteger p = publicKey.getParams().getP(); @@ -206,8 +228,8 @@ public class DSASignature extends SignatureSpi return true; else return false; - } catch ( DEREncodingException deree ) { - throw new SignatureException(); + } catch (IOException ioe) { + throw new SignatureException("badly formed signature"); } } diff --git a/libjava/gnu/java/security/provider/Gnu.java b/libjava/gnu/java/security/provider/Gnu.java index c556107..70a7d1d 100644 --- a/libjava/gnu/java/security/provider/Gnu.java +++ b/libjava/gnu/java/security/provider/Gnu.java @@ -1,5 +1,5 @@ /* Gnu.java --- Gnu provider main class - Copyright (C) 1999, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,7 +43,7 @@ public final class Gnu extends Provider { public Gnu() { - super( "GNU", 1.0, "GNU provider v1.0 implementing SHA-1, MD5, DSA"); + super("GNU", 1.0, "GNU provider v1.0 implementing SHA-1, MD5, DSA, X.509 Certificates"); // Note that all implementation class names are referenced by using // Class.getName(). That way when we staticly link the Gnu provider @@ -51,7 +51,7 @@ public final class Gnu extends Provider // Signature put("Signature.SHA1withDSA", - gnu.java.security.provider.DSASignature.class.getName()); + gnu.java.security.provider.DSASignature.class.getName()); put("Alg.Alias.Signature.DSS", "SHA1withDSA"); put("Alg.Alias.Signature.DSA", "SHA1withDSA"); @@ -68,12 +68,20 @@ public final class Gnu extends Provider // Key Pair Generator put("KeyPairGenerator.DSA", - gnu.java.security.provider.DSAKeyPairGenerator.class.getName()); + gnu.java.security.provider.DSAKeyPairGenerator.class.getName()); put("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); put("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); put("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); + // Key Factory + put("KeyFactory.DSA", + gnu.java.security.provider.DSAKeyFactory.class.getName()); + + put("Alg.Alias.KeyFactory.OID.1.2.840.10040.4.1", "DSA"); + put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA"); + put("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA"); + // Message Digests put("MessageDigest.SHA", gnu.java.security.provider.SHA.class.getName()); put("MessageDigest.MD5", gnu.java.security.provider.MD5.class.getName()); @@ -84,15 +92,20 @@ public final class Gnu extends Provider // Algorithm Parameters put("AlgorithmParameters.DSA", - gnu.java.security.provider.DSAParameters.class.getName()); + gnu.java.security.provider.DSAParameters.class.getName()); // Algorithm Parameter Generator put("AlgorithmParameterGenerator.DSA", - gnu.java.security.provider.DSAParameterGenerator.class.getName()); + gnu.java.security.provider.DSAParameterGenerator.class.getName()); // SecureRandom put("SecureRandom.SHA1PRNG", - gnu.java.security.provider.SHA1PRNG.class.getName()); + gnu.java.security.provider.SHA1PRNG.class.getName()); + + // CertificateFactory + put("CertificateFactory.X.509", + gnu.java.security.provider.X509CertificateFactory.class.getName()); + put("Alg.Alias.CertificateFactory.X509", "X.509"); } } diff --git a/libjava/gnu/java/security/provider/GnuDSAPrivateKey.java b/libjava/gnu/java/security/provider/GnuDSAPrivateKey.java index 45446c7..4eceb00 100644 --- a/libjava/gnu/java/security/provider/GnuDSAPrivateKey.java +++ b/libjava/gnu/java/security/provider/GnuDSAPrivateKey.java @@ -82,4 +82,10 @@ public class GnuDSAPrivateKey implements DSAPrivateKey { return x; } + + public String toString() + { + return "GnuDSAPrivateKey: x=" + x.toString(16) + " p=" + p.toString(16) + + " q=" + q.toString(16) + " g=" + g.toString(16); + } } diff --git a/libjava/gnu/java/security/provider/GnuDSAPublicKey.java b/libjava/gnu/java/security/provider/GnuDSAPublicKey.java index 2f23abb..91d6b56 100644 --- a/libjava/gnu/java/security/provider/GnuDSAPublicKey.java +++ b/libjava/gnu/java/security/provider/GnuDSAPublicKey.java @@ -1,5 +1,5 @@ /* GnuDSAPublicKey.java --- Gnu DSA Public Key - Copyright (C) 1999 Free Software Foundation, Inc. + Copyright (C) 1999,2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -82,4 +82,10 @@ public class GnuDSAPublicKey implements DSAPublicKey { return y; } + + public String toString() + { + return "GnuDSAPublicKey: y=" + y.toString(16) + " p=" + p.toString(16) + + " q=" + q.toString(16) + " g=" + g.toString(16); + } } diff --git a/libjava/gnu/java/security/provider/X509CertificateFactory.java b/libjava/gnu/java/security/provider/X509CertificateFactory.java new file mode 100644 index 0000000..62d3d38 --- /dev/null +++ b/libjava/gnu/java/security/provider/X509CertificateFactory.java @@ -0,0 +1,269 @@ +/* X509CertificateFactory.java -- generates X.509 certificates. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.provider; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.InputStream; +import java.io.IOException; + +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactorySpi; +import java.security.cert.CRL; +import java.security.cert.CRLException; + +import java.util.Collection; +import java.util.LinkedList; + +import gnu.java.io.Base64InputStream; +import gnu.java.security.x509.X509Certificate; +import gnu.java.security.x509.X509CRL; + +public class X509CertificateFactory extends CertificateFactorySpi +{ + + // Constants. + // ------------------------------------------------------------------------ + + public static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----"; + public static final String END_CERTIFICATE = "-----END CERTIFICATE-----"; + public static final String BEGIN_X509_CRL = "-----BEGIN X509 CRL-----"; + public static final String END_X509_CRL = "-----END X509 CRL-----"; + + // Constructors. + // ------------------------------------------------------------------------ + + public X509CertificateFactory() + { + super(); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public Certificate engineGenerateCertificate(InputStream inStream) + throws CertificateException + { + try + { + return generateCert(inStream); + } + catch (IOException ioe) + { + throw new CertificateException(ioe.toString()); + } + } + + public Collection engineGenerateCertificates(InputStream inStream) + throws CertificateException + { + LinkedList certs = new LinkedList(); + while (true) + { + try + { + certs.add(generateCert(inStream)); + } + catch (EOFException eof) + { + break; + } + catch (IOException ioe) + { + throw new CertificateException(ioe.toString()); + } + } + return certs; + } + + public CRL engineGenerateCRL(InputStream inStream) throws CRLException + { + try + { + return generateCRL(inStream); + } + catch (IOException ioe) + { + throw new CRLException(ioe.toString()); + } + } + + public Collection engineGenerateCRLs(InputStream inStream) + throws CRLException + { + LinkedList crls = new LinkedList(); + while (true) + { + try + { + crls.add(generateCRL(inStream)); + } + catch (EOFException eof) + { + break; + } + catch (IOException ioe) + { + throw new CRLException(ioe.toString()); + } + } + return crls; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private X509Certificate generateCert(InputStream inStream) + throws IOException, CertificateException + { + if (!inStream.markSupported()) + inStream = new BufferedInputStream(inStream, 8192); + inStream.mark(20); + int i = inStream.read(); + if (i == -1) + throw new EOFException(); + + // If the input is in binary DER format, the first byte MUST be + // 0x30, which stands for the ASN.1 [UNIVERSAL 16], which is the + // UNIVERSAL SEQUENCE, with the CONSTRUCTED bit (0x20) set. + // + // So if we do not see 0x30 here we will assume it is in Base-64. + if (i != 0x30) + { + inStream.reset(); + StringBuffer line = new StringBuffer(80); + do + { + line.setLength(0); + do + { + i = inStream.read(); + if (i == -1) + throw new EOFException(); + if (i != '\n' && i != '\r') + line.append((char) i); + } + while (i != '\n' && i != '\r'); + } + while (!line.toString().equals(BEGIN_CERTIFICATE)); + X509Certificate ret = new X509Certificate( + new BufferedInputStream(new Base64InputStream(inStream), 8192)); + line.setLength(0); + line.append('-'); // Base64InputStream will eat this. + do + { + i = inStream.read(); + if (i == -1) + throw new EOFException(); + if (i != '\n' && i != '\r') + line.append((char) i); + } + while (i != '\n' && i != '\r'); + // XXX ??? + if (!line.toString().equals(END_CERTIFICATE)) + throw new CertificateException("no end-of-certificate marker"); + return ret; + } + else + { + inStream.reset(); + return new X509Certificate(inStream); + } + } + + private X509CRL generateCRL(InputStream inStream) + throws IOException, CRLException + { + if (!inStream.markSupported()) + inStream = new BufferedInputStream(inStream, 8192); + inStream.mark(20); + int i = inStream.read(); + if (i == -1) + throw new EOFException(); + + // If the input is in binary DER format, the first byte MUST be + // 0x30, which stands for the ASN.1 [UNIVERSAL 16], which is the + // UNIVERSAL SEQUENCE, with the CONSTRUCTED bit (0x20) set. + // + // So if we do not see 0x30 here we will assume it is in Base-64. + if (i != 0x30) + { + inStream.reset(); + StringBuffer line = new StringBuffer(80); + do + { + line.setLength(0); + do + { + i = inStream.read(); + if (i == -1) + throw new EOFException(); + if (i != '\n' && i != '\r') + line.append((char) i); + } + while (i != '\n' && i != '\r'); + } + while (!line.toString().startsWith(BEGIN_X509_CRL)); + X509CRL ret = new X509CRL( + new BufferedInputStream(new Base64InputStream(inStream), 8192)); + line.setLength(0); + line.append('-'); // Base64InputStream will eat this. + do + { + i = inStream.read(); + if (i == -1) + throw new EOFException(); + if (i != '\n' && i != '\r') + line.append((char) i); + } + while (i != '\n' && i != '\r'); + // XXX ??? + if (!line.toString().startsWith(END_X509_CRL)) + throw new CRLException("no end-of-CRL marker"); + return ret; + } + else + { + inStream.reset(); + return new X509CRL(inStream); + } + } + +} diff --git a/libjava/gnu/java/security/x509/X500DistinguishedName.java b/libjava/gnu/java/security/x509/X500DistinguishedName.java new file mode 100644 index 0000000..5284385 --- /dev/null +++ b/libjava/gnu/java/security/x509/X500DistinguishedName.java @@ -0,0 +1,824 @@ +/* X500DistinguishedName.java -- X.500 name. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.x509; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.StreamTokenizer; +import java.io.StringReader; + +import java.security.Principal; + +import java.util.HashSet; +import java.util.LinkedList; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; + +/** + * A X.500 distinguished name. Distinguished names are sequences of + * ATTRIB=VALUE pairs, where ATTRIB is one of the following: + * + * <table cellpadding="0" cellspacing="0" border="0"> + * <tr> + * <th bgcolor="#CCCCFF" align="left">Name</th> + * <th bgcolor="#CCCCFF" align="left">X.500 AttributeType</th> + * <th bgcolor="#CCCCFF" align="left">ObjectIdentifier</th> + * </tr> + * <tr> + * <td align="left">CN</td> + * <td align="left">commonName</td> + * <td align="left">2.5.4.3</td> + * </tr> + * <tr> + * <td align="left">C</td> + * <td align="left">countryName</td> + * <td align="left">2.5.4.6</td> + * </tr> + * <tr> + * <td align="left">L</td> + * <td align="left">localityName</td> + * <td align="left">2.5.4.7</td> + * </tr> + * <tr> + * <td align="left">ST</td> + * <td align="left">stateOrProvinceName</td> + * <td align="left">2.5.4.8</td> + * </tr> + * <tr> + * <td align="left">STREET</td> + * <td align="left">streetAddress</td> + * <td align="left">2.5.4.9</td> + * </tr> + * <tr> + * <td align="left">O</td> + * <td align="left">organizationName</td> + * <td align="left">2.5.4.10</td> + * </tr> + * <tr> + * <td align="left">OU</td> + * <td align="left">organizationUnitName</td> + * <td align="left">2.5.4.11</td> + * </tr> + * <tr> + * <td align="left">DC</td> + * <td align="left">domainComponent</td> + * <td align="left">0.9.2342.19200300.100.1.25</td> + * </tr> + * <tr> + * <td align="left">UID</td> + * <td align="left">userid</td> + * <td align="left"0.9.2342.19200300.100.1.1></td> + * </tr> + * <tr> + * <td align="left">DNQ or DNQUALIFIER(*)</td> + * <td align="left">domainNameQualifier</td> + * <td align="left">2.5.4.46</td> + * </tr> + * <tr> + * <td align="left">SURNAME(*)</td> + * <td align="left">name</td> + * <td align="left">2.5.4.41</td> + * </tr> + * <tr> + * <td align="left">GIVENNAME(*)</td> + * <td align="left">givenName</td> + * <td align="left">2.5.4.42</td> + * </tr> + * <tr> + * <td align="left">INITIALS(*)</td> + * <td align="left">initials</td> + * <td align="left">2.5.4.43</td> + * </tr> + * <tr> + * <td align="left">EMAILADDRESS(*)</td> + * <td align="left">emailAddress</td> + * <td align="left">2.5.4.44</td> + * </tr> + * </table> + * + * <p><i>(*) = attributes not specified in RFC1779 or RFC2253, but + * recognized anyway.</i> + * + * <p>Distinguished names of this form are used in the lightweight + * directory access protocol (LDAP) and in the issuer and subject fields + * of X.509 certificates. + * + * @author Casey Marshall (rsdio@metastatic.org) + * @see javax.security.auth.x500.X500Principal + * @status DER decoding/encoding works, RFC1779 and RFC2253 need to be + * made more robust. + */ +public class X500DistinguishedName +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + public static final OID CN = new OID("2.5.4.3"); + public static final OID C = new OID("2.5.4.6"); + public static final OID L = new OID("2.5.4.7"); + public static final OID ST = new OID("2.5.4.8"); + public static final OID STREET = new OID("2.5.4.9"); + public static final OID O = new OID("2.5.4.10"); + public static final OID OU = new OID("2.5.4.11"); + public static final OID T = new OID("2.5.4.12"); + public static final OID DNQ = new OID("2.5.4.46"); + public static final OID NAME = new OID("2.5.4.41"); + public static final OID GIVENNAME = new OID("2.5.4.42"); + public static final OID INITIALS = new OID("2.5.4.43"); + public static final OID GENERATION = new OID("2.5.4.44"); + public static final OID EMAIL = new OID("1.2.840.113549.1.9.1"); + public static final OID DC = new OID("0.9.2342.19200300.100.1.25"); + public static final OID UID = new OID("0.9.2342.19200300.100.1.1"); + + private String commonName; + private String country; + private String locality; + private String orgUnit; + private String organization; + private String street; + private String state; + private String title; + private String dnQualifier; + private String surname; + private String givenName; + private String initials; + private String generation; + private String email; + private String domainComponent; + private String userid; + + private String nameRFC1779; + private String nameRFC2253; + private String nameCanonical; + + private transient byte[] encoded; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new X500DistinguishedName from the RFC1779 or RFC2253 + * encoded form. + * + * @param name The encoded name. + * @throws IllegalArgumentException If the name cannot be parsed. + */ + public X500DistinguishedName(String name) + { + if (name == null) + throw new NullPointerException(); + try + { + parseDN(name, true); + } + catch (Exception e) + { + parseDN(name, false); + } + } + + /** + * Create a new X500DistinguishedName from the DER encoded bytes. + * + * @param encoded The encoded form. + * @throws IOException If the bytes are not a valid DER construct. + */ + public X500DistinguishedName(byte[] encoded) throws IOException + { + this(new ByteArrayInputStream(encoded)); + } + + /** + * Create a new X500DistinguishedName from the DER encoded bytes. + * + * @param encoded The encoded form. + * @throws IOException If the bytes are not a valid DER construct. + */ + public X500DistinguishedName(InputStream encoded) throws IOException + { + parseDER(encoded); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public boolean equals(Object o) + { + return + (commonName != null && + commonName.equals(((X500DistinguishedName) o).commonName)) && + (country != null && + country.equals(((X500DistinguishedName) o).country)) && + (locality != null && + locality.equals(((X500DistinguishedName) o).locality)) && + (orgUnit != null && + orgUnit.equals(((X500DistinguishedName) o).orgUnit)) && + (organization != null && + organization.equals(((X500DistinguishedName) o).organization)) && + (street != null && + street.equals(((X500DistinguishedName) o).street)) && + (state != null && + state.equals(((X500DistinguishedName) o).state)) && + (domainComponent != null && + domainComponent.equals(((X500DistinguishedName) o).domainComponent)) && + (title != null && + title.equals(((X500DistinguishedName) o).title)) && + (dnQualifier != null && + dnQualifier.equals(((X500DistinguishedName) o).dnQualifier)) && + (surname != null && + surname.equals(((X500DistinguishedName) o).surname)) && + (givenName != null && + givenName.equals(((X500DistinguishedName) o).givenName)) && + (initials != null && + initials.equals(((X500DistinguishedName) o).initials)) && + (generation != null && + generation.equals(((X500DistinguishedName) o).generation)) && + (email != null && + email.equals(((X500DistinguishedName) o).email)) && + (userid != null && + userid.equals(((X500DistinguishedName) o).userid)); + } + + public byte[] getEncoded() + { + if (encoded == null) + encoded = encodeDER(); + return (byte[]) encoded.clone(); + } + + private static String quote(String str) + { + if (str.indexOf(" ") > 0 || str.indexOf("\f") > 0 || + str.indexOf("\n") > 0 || str.indexOf("\r") > 0 || + str.indexOf("\t") > 0) + str = '"' + str + '"'; + // XXX needs regex + //return str.replaceAll("([,+\"\\<>;])", "\\\1"); + return str; + } + + public String toRFC1779() + { + if (nameRFC1779 != null) + return nameRFC1779; + StringBuffer buf = new StringBuffer(); + if (commonName != null) + buf.append("CN=").append(quote(commonName)).append(", "); + if (country != null) + buf.append("C=").append(quote(country)).append(", "); + if (locality != null) + buf.append("L=").append(quote(locality)).append(", "); + if (orgUnit != null) + buf.append("OU=").append(quote(orgUnit)).append(", "); + if (organization != null) + buf.append("O=").append(quote(organization)).append(", "); + if (street != null) + buf.append("STREET=").append(quote(street)).append(", "); + if (state != null) + buf.append("ST=").append(quote(state)).append(", "); + if (title != null) + buf.append(T).append("=").append(quote(title)).append(", "); + if (dnQualifier != null) + buf.append(DNQ).append("=").append(quote(dnQualifier)).append(", "); + if (surname != null) + buf.append(NAME).append("=").append(quote(surname)).append(", "); + if (givenName != null) + buf.append(GIVENNAME).append("=").append(quote(givenName)).append(", "); + if (initials != null) + buf.append(INITIALS).append("=").append(quote(initials)).append(", "); + if (generation != null) + buf.append(GENERATION).append("=").append(quote(generation)).append(", "); + if (email != null) + buf.append(EMAIL).append("=").append(quote(email)).append(", "); + if (domainComponent != null) + buf.append(DC).append("=").append(quote(domainComponent)).append(", "); + if (userid != null) + buf.append(UID).append("=").append(quote(userid)).append(", "); + // XXX escapes + return (nameRFC1779 = buf.substring(0, buf.length()-2)); + } + + public String toRFC2253() + { + if (nameRFC2253 != null) + return nameRFC2253; + StringBuffer buf = new StringBuffer(); + if (commonName != null) + buf.append("CN=").append(quote(commonName)).append(","); + if (country != null) + buf.append("C=").append(quote(country)).append(","); + if (locality != null) + buf.append("L=").append(quote(locality)).append(","); + if (orgUnit != null) + buf.append("OU=").append(quote(orgUnit)).append(","); + if (organization != null) + buf.append("O=").append(quote(organization)).append(","); + if (street != null) + buf.append("STREET=").append(quote(street)).append(","); + if (state != null) + buf.append("ST=").append(quote(state)).append(","); + if (title != null) + buf.append(T).append("=").append(quote(title)).append(","); + if (dnQualifier != null) + buf.append(DNQ).append("=").append(quote(dnQualifier)).append(","); + if (surname != null) + buf.append(NAME).append("=").append(quote(surname)).append(","); + if (givenName != null) + buf.append(GIVENNAME).append("=").append(quote(givenName)).append(","); + if (initials != null) + buf.append(INITIALS).append("=").append(quote(initials)).append(","); + if (generation != null) + buf.append(GENERATION).append("=").append(quote(generation)).append(","); + if (email != null) + buf.append(EMAIL).append("=").append(quote(email)).append(","); + if (domainComponent != null) + buf.append(DC).append("=").append(quote(domainComponent)).append(","); + if (userid != null) + buf.append(UID).append("=").append(quote(userid)).append(","); + // XXX escapes. + return (nameRFC2253 = buf.substring(0, buf.length()-1)); + } + + public String toCanonical() + { + if (nameCanonical != null) + return nameCanonical; + nameCanonical = toRFC2253(); + return nameCanonical; // XXX canonicalize + } + + public String getCommonName() + { + return commonName; + } + + public String getCountry() + { + return country; + } + + public String getLocality() + { + return locality; + } + + public String getOrganizationalUnit() + { + return orgUnit; + } + + public String getOrganization() + { + return organization; + } + + public String getStreet() + { + return street; + } + + public String getState() + { + return state; + } + + public String getTitle() + { + return title; + } + + public String getDNQualifier() + { + return dnQualifier; + } + + public String getSurname() + { + return surname; + } + + public String getGivenName() + { + return givenName; + } + + public String getInitials() + { + return initials; + } + + public String getGeneration() + { + return generation; + } + + public String getEmail() + { + return email; + } + + public String getDomain() + { + return domainComponent; + } + + public String getUserID() + { + return userid; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private static String unquote(String str) + { + if (str.startsWith("\"") && str.endsWith("\"")) + str = str.substring(1, str.length()-1); + // XXX needs regex + //return str.replaceAll("\\([,+\"\\<>;])", "\1"); + return str; + } + + private void parseDN(String name, boolean rfc2253) + { + if (name.length() == 0) + throw new IllegalArgumentException("zero-length distinguished name"); + StreamTokenizer parse = new StreamTokenizer(new StringReader(name)); + parse.resetSyntax(); + parse.wordChars('\000', '~'); + parse.ordinaryChar('#'); + parse.ordinaryChar(','); + parse.ordinaryChar('='); + parse.ordinaryChar('<'); + parse.ordinaryChar('>'); + parse.ordinaryChar(';'); + parse.ordinaryChar('\\'); + parse.quoteChar('"'); + String attrib = null; + String value = null; + int token, lastToken = ','; + while (true) + { + try + { + token = parse.nextToken(); + } + catch (IOException ioe) + { + throw new IllegalArgumentException(); + } + switch (token) + { + case StreamTokenizer.TT_WORD: + if (lastToken == ',' || lastToken == '+' || + (!rfc2253 && lastToken == ';')) + attrib = parse.sval.trim(); + else if (lastToken == '=') + value = unquote(parse.sval.trim()); + else + throw new IllegalArgumentException(); + break; + case '"': + if (lastToken == '=') + value = parse.sval; + else + throw new IllegalArgumentException(); + break; + case ';': + if (rfc2253) + throw new IllegalArgumentException(); + case ',': + case '+': + if (attrib == null || value == null) + throw new IllegalArgumentException("extraneous separator"); + try + { + setAttribute(new OID(attrib), value); + } + catch (Exception x) + { + setAttribute(attrib, value); + } + attrib = null; + value = null; + break; + case '=': + break; + case StreamTokenizer.TT_EOF: + return; + default: + throw new IllegalArgumentException("unknown token " + (char)token + + " (" + token + ")"); + } + lastToken = token; + } + } + + private void parseDER(InputStream in) throws IOException + { + DERReader der = new DERReader(in); + DERValue name = der.read(); + if (!name.isConstructed()) + throw new ASN1ParsingException("badly formed Name"); + int len = 0; + while (len < name.getLength()) + { + DERValue rdn = der.read(); + if (rdn.getValue() != DER.CONSTRUCTED_VALUE) + throw new ASN1ParsingException("badly formed RDNSequence"); + int len2 = 0; + while (len2 < rdn.getLength()) + { + DERValue atav = der.read(); + if (atav.getValue() != DER.CONSTRUCTED_VALUE) + throw new ASN1ParsingException( + "badly formed AttributeTypeAndValue"); + OID atype = (OID) der.read().getValue(); + String aval = (String) der.read().getValue(); + setAttribute(atype, aval); + len2 += 1 + atav.getLength() + + DERWriter.definiteEncodingSize(atav.getLength()); + } + len += len2 + 1 + DERWriter.definiteEncodingSize(name.getLength()); + } + } + + private byte[] encodeDER() + { + try + { + LinkedList name = new LinkedList(); + if (commonName != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, CN)); + atav.add(new DERValue(DER.PRINTABLE_STRING, commonName)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (country != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, C)); + atav.add(new DERValue(DER.PRINTABLE_STRING, country)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (locality != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, L)); + atav.add(new DERValue(DER.PRINTABLE_STRING, locality)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (orgUnit != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, OU)); + atav.add(new DERValue(DER.PRINTABLE_STRING, orgUnit)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (organization != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, O)); + atav.add(new DERValue(DER.PRINTABLE_STRING, organization)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (street != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, STREET)); + atav.add(new DERValue(DER.PRINTABLE_STRING, street)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (state != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, ST)); + atav.add(new DERValue(DER.PRINTABLE_STRING, state)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (title != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, T)); + atav.add(new DERValue(DER.PRINTABLE_STRING, title)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (dnQualifier != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, DNQ)); + atav.add(new DERValue(DER.PRINTABLE_STRING, dnQualifier)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (surname != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, NAME)); + atav.add(new DERValue(DER.PRINTABLE_STRING, surname)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (givenName != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, GIVENNAME)); + atav.add(new DERValue(DER.PRINTABLE_STRING, givenName)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (initials != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, INITIALS)); + atav.add(new DERValue(DER.PRINTABLE_STRING, initials)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (generation != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, GENERATION)); + atav.add(new DERValue(DER.PRINTABLE_STRING, generation)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (email != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, EMAIL)); + atav.add(new DERValue(DER.PRINTABLE_STRING, email)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (domainComponent != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, DC)); + atav.add(new DERValue(DER.PRINTABLE_STRING, domainComponent)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + if (userid != null) + { + HashSet rdn = new HashSet(); + LinkedList atav = new LinkedList(); + atav.add(new DERValue(DER.OBJECT_IDENTIFIER, UID)); + atav.add(new DERValue(DER.PRINTABLE_STRING, userid)); + rdn.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, atav)); + name.add(new DERValue(DER.CONSTRUCTED | DER.SET, rdn)); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DERWriter.write(out, new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, name)); + return out.toByteArray(); + } + catch (IOException ioe) + { + throw new Error(ioe); + } + } + + private void setAttribute(String atype, String aval) + { + if (atype.equals("CN")) + commonName = aval; + else if (atype.equals("C")) + country = aval; + else if (atype.equals("L")) + locality = aval; + else if (atype.equals("ST")) + state = aval; + else if (atype.equals("STREET")) + street = aval; + else if (atype.equals("O")) + organization = aval; + else if (atype.equals("OU")) + orgUnit = aval; + else if (atype.equals("T")) + title = aval; + else if (atype.equals("DNQ") || atype.equals("DNQUALIFIER")) + dnQualifier = aval; + else if (atype.equals("SURNAME")) + surname = aval; + else if (atype.equals("GIVENNAME")) + givenName = aval; + else if (atype.equals("INITIALS")) + initials = aval; + else if (atype.equals("GENERATION")) + generation = aval; + else if (atype.equals("EMAILADDRESS")) + email = aval; + else if (atype.equals("DC")) + domainComponent = aval; + else if (atype.equals("UID")) + userid = aval; + else + throw new IllegalArgumentException("unknown attribute " + atype); + } + + private void setAttribute(OID atype, String aval) + { + if (atype.equals(CN)) + commonName = aval; + else if (atype.equals(C)) + country = aval; + else if (atype.equals(L)) + locality = aval; + else if (atype.equals(ST)) + state = aval; + else if (atype.equals(STREET)) + street = aval; + else if (atype.equals(O)) + organization = aval; + else if (atype.equals(OU)) + orgUnit = aval; + else if (atype.equals(T)) + title = aval; + else if (atype.equals(DNQ)) + dnQualifier = aval; + else if (atype.equals(NAME)) + surname = aval; + else if (atype.equals(GIVENNAME)) + givenName = aval; + else if (atype.equals(INITIALS)) + initials = aval; + else if (atype.equals(GENERATION)) + generation = aval; + else if (atype.equals(EMAIL)) + email = aval; + else if (atype.equals(DC)) + domainComponent = aval; + else if (atype.equals(UID)) + userid = aval; + else + throw new IllegalArgumentException("unknown attribute " + atype); + } +} diff --git a/libjava/gnu/java/security/x509/X509CRL.java b/libjava/gnu/java/security/x509/X509CRL.java new file mode 100644 index 0000000..6205c0f --- /dev/null +++ b/libjava/gnu/java/security/x509/X509CRL.java @@ -0,0 +1,404 @@ +/* X509CRL.java -- X.509 certificate revocation list. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.x509; + +import java.io.InputStream; +import java.io.IOException; + +import java.math.BigInteger; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Set; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Principal; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CRLException; +import java.security.cert.X509CRLEntry; + +import javax.security.auth.x500.X500Principal; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.BitString; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; + +/** + * X.509 certificate revocation lists. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class X509CRL extends java.security.cert.X509CRL +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + private static final OID ID_DSA = new OID("1.2.840.10040.4.1"); + private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3"); + private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1"); + private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2"); + private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4"); + private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5"); + + private byte[] encoded; + + private byte[] tbsCRLBytes; + private int version; + private OID algId; + private byte[] algParams; + private Date thisUpdate; + private Date nextUpdate; + private X500Principal issuerDN; + private HashMap revokedCerts; + private HashMap extensions; + private HashSet critOids; + private HashSet nonCritOids; + + private OID sigAlg; + private byte[] sigAlgParams; + private byte[] rawSig; + private byte[] signature; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new X.509 CRL. + * + * @param encoded The DER encoded CRL. + * @throws CRLException If the input bytes are incorrect. + * @throws IOException If the input bytes cannot be read. + */ + public X509CRL(InputStream encoded) throws CRLException, IOException + { + super(); + revokedCerts = new HashMap(); + extensions = new HashMap(); + critOids = new HashSet(); + nonCritOids = new HashSet(); + try + { + parse(encoded); + } + catch (IOException ioe) + { + ioe.printStackTrace(); + throw ioe; + } + catch (Exception x) + { + x.printStackTrace(); + throw new CRLException(x.toString()); + } + } + + // X509CRL methods. + // ------------------------------------------------------------------------ + + public boolean equals(Object o) + { + return ((X509CRL) o).revokedCerts.equals(revokedCerts); + } + + public int hashCode() + { + return revokedCerts.hashCode(); + } + + public byte[] getEncoded() throws CRLException + { + return (byte[]) encoded.clone(); + } + + public void verify(PublicKey key) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException + { + Signature sig = Signature.getInstance(sigAlg.toString()); + doVerify(sig, key); + } + + public void verify(PublicKey key, String provider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException + { + Signature sig = Signature.getInstance(sigAlg.toString(), provider); + doVerify(sig, key); + } + + public int getVersion() + { + return version; + } + + public Principal getIssuerDN() + { + return issuerDN; + } + + public X500Principal getIssuerX500Principal() + { + return issuerDN; + } + + public Date getThisUpdate() + { + return (Date) thisUpdate.clone(); + } + + public Date getNextUpdate() + { + if (nextUpdate != null) + return (Date) nextUpdate.clone(); + return null; + } + + public X509CRLEntry getRevokedCertificate(BigInteger serialNo) + { + return (X509CRLEntry) revokedCerts.get(serialNo); + } + + public Set getRevokedCertificates() + { + return Collections.unmodifiableSet(new HashSet(revokedCerts.values())); + } + + public byte[] getTBSCertList() throws CRLException + { + return (byte[]) tbsCRLBytes.clone(); + } + + public byte[] getSignature() + { + return (byte[]) rawSig.clone(); + } + + public String getSigAlgName() + { + if (sigAlg.equals(ID_DSA_WITH_SHA1)) + return "SHA1withDSA"; + if (sigAlg.equals(ID_RSA_WITH_MD2)) + return "MD2withRSA"; + if (sigAlg.equals(ID_RSA_WITH_MD5)) + return "MD5withRSA"; + if (sigAlg.equals(ID_RSA_WITH_SHA1)) + return "SHA1withRSA"; + return "unknown"; + } + + public String getSigAlgOID() + { + return sigAlg.toString(); + } + + public byte[] getSigAlgParams() + { + if (sigAlgParams != null) + return (byte[]) sigAlgParams.clone(); + return null; + } + + // X509Extension methods. + // ------------------------------------------------------------------------ + + public boolean hasUnsupportedCriticalExtension() + { + return false; // XXX + } + + public Set getCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(critOids); + } + + public Set getNonCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(nonCritOids); + } + + public byte[] getExtensionValue(String oid) + { + byte[] ext = (byte[]) extensions.get(oid); + if (ext != null) + return (byte[]) ext.clone(); + return null; + } + + // CRL methods. + // ------------------------------------------------------------------------ + + public String toString() + { + return gnu.java.security.x509.X509CRL.class.getName(); + } + + public boolean isRevoked(Certificate cert) + { + if (!(cert instanceof java.security.cert.X509Certificate)) + throw new IllegalArgumentException("not a X.509 certificate"); + BigInteger certSerial = + ((java.security.cert.X509Certificate) cert).getSerialNumber(); + X509CRLEntry ent = (X509CRLEntry) revokedCerts.get(certSerial); + if (ent == null) + return false; + return ent.getRevocationDate().compareTo(new Date()) < 0; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private void doVerify(Signature sig, PublicKey key) + throws CRLException, InvalidKeyException, SignatureException + { + sig.initVerify(key); + sig.update(tbsCRLBytes); + if (!sig.verify(signature)) + throw new CRLException("signature not verified"); + } + + private void parse(InputStream in) throws Exception + { + DERReader der = new DERReader(in); + DERValue val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed CertificateList"); + encoded = val.getEncoded(); + + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed TBSCertList"); + tbsCRLBytes = val.getEncoded(); + + val = der.read(); + if (val.getValue() instanceof BigInteger) + { + version = ((BigInteger) val.getValue()).intValue() + 1; + val = der.read(); + } + else + version = 1; + + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed AlgorithmIdentifier"); + DERValue algIdVal = der.read(); + algId = (OID) algIdVal.getValue(); + if (val.getLength() > algIdVal.getEncodedLength()) + { + val = der.read(); + algParams = val.getEncoded(); + if (val.isConstructed()) + in.skip(val.getLength()); + } + + issuerDN = new X500Principal(in); + + thisUpdate = (Date) der.read().getValue(); + + val = der.read(); + if (val.getValue() instanceof Date) + { + nextUpdate = (Date) val.getValue(); + val = der.read(); + } + if (val.getTag() != 0) + { + int len = 0; + while (len < val.getLength()) + { + X509CRLEntry entry = + new gnu.java.security.x509.X509CRLEntry(version, in); + revokedCerts.put(entry.getSerialNumber(), entry); + len += entry.getEncoded().length; + } + } + if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 0) + { + val = der.read(); + int len = 0; + while (len < val.getLength()) + { + DERValue ext = der.read(); + OID extId = (OID) der.read().getValue(); + DERValue val2 = der.read(); + Boolean crit = Boolean.valueOf(false); + if (val2.getValue() instanceof Boolean) + { + crit = (Boolean) val2.getValue(); + val2 = der.read(); + } + byte[] extVal = (byte[]) val2.getValue(); + extensions.put(extId.toString(), extVal); + if (crit.booleanValue()) + critOids.add(extId.toString()); + else + nonCritOids.add(extId.toString()); + len += ext.getEncodedLength(); + } + } + + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed AlgorithmIdentifier"); + DERValue sigAlgVal = der.read(); + sigAlg = (OID) sigAlgVal.getValue(); + if (val.getLength() > sigAlgVal.getEncodedLength()) + { + val = der.read(); + sigAlgParams = (byte[]) val.getEncoded(); + if (val.isConstructed()) + in.skip(val.getLength()); + } + val = der.read(); + rawSig = val.getEncoded(); + signature = ((BitString) val.getValue()).toByteArray(); + } +} diff --git a/libjava/gnu/java/security/x509/X509CRLEntry.java b/libjava/gnu/java/security/x509/X509CRLEntry.java new file mode 100644 index 0000000..4057c60 --- /dev/null +++ b/libjava/gnu/java/security/x509/X509CRLEntry.java @@ -0,0 +1,236 @@ +/* X509CRLEntry.java -- entry in a X.509 CRL. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.x509; + +import java.io.InputStream; +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.cert.CRLException; + +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; + +/** + * A single entry in a X.509 certificate revocation list. + * + * @see X509CRL + * @author Casey Marshall + */ +class X509CRLEntry extends java.security.cert.X509CRLEntry +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** The DER encoded form of this CRL entry. */ + private byte[] encoded; + + /** The revoked certificate's serial number. */ + private BigInteger serialNo; + + /** The date the certificate was revoked. */ + private Date revocationDate; + + /** The encoded extensions. */ + private HashMap extensions; + + /** The set of critical extension OIDs. */ + private HashSet critOids; + + /** the set of non-critical extension OIDs. */ + private HashSet nonCritOids; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new X.509 certificate revocation list entry from the given + * input stream and CRL version number. + * + * @param version The CRL version. + * @param encoded The stream of DER bytes. + * @throws CRLException If the ASN.1 structure is invalid. + * @throws IOException If the bytes cannot be read. + */ + X509CRLEntry(int version, InputStream encoded) + throws CRLException, IOException + { + super(); + extensions = new HashMap(); + critOids = new HashSet(); + nonCritOids = new HashSet(); + try + { + parse(version, encoded); + } + catch (IOException ioe) + { + throw ioe; + } + catch (Exception x) + { + throw new CRLException(x.toString()); + } + } + + // X509CRLEntry methods. + // ------------------------------------------------------------------------ + + public boolean equals(Object o) + { + return ((X509CRLEntry) o).serialNo.equals(serialNo) && + ((X509CRLEntry) o).revocationDate.equals(revocationDate); + } + + public int hashCode() + { + return serialNo.hashCode(); + } + + public byte[] getEncoded() throws CRLException + { + return (byte[]) encoded.clone(); + } + + public BigInteger getSerialNumber() + { + return serialNo; + } + + public Date getRevocationDate() + { + return (Date) revocationDate.clone(); + } + + public boolean hasExtensions() + { + return ! extensions.isEmpty(); + } + + public String toString() + { + return "X509CRLEntry serial=" + serialNo + " revocation date=" + + revocationDate + " critExt=" + critOids + " ext=" + nonCritOids; + } + + // X509Extension methods. + // ------------------------------------------------------------------------ + + public boolean hasUnsupportedCriticalExtension() + { + return false; // XXX + } + + public Set getCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(critOids); + } + + public Set getNonCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(nonCritOids); + } + + public byte[] getExtensionValue(String oid) + { + byte[] ext = (byte[]) extensions.get(oid); + if (ext != null) + return (byte[]) ext.clone(); + return null; + } + + // Own methods. + // ------------------------------------------------------------------------ + + private void parse(int version, InputStream in) throws Exception + { + DERReader der = new DERReader(in); + DERValue entry = der.read(); + if (!entry.isConstructed()) + throw new ASN1ParsingException("malformed revokedCertificate"); + encoded = entry.getEncoded(); + int len = 0; + DERValue val = der.read(); + serialNo = (BigInteger) val.getValue(); + len += DERWriter.definiteEncodingSize(val.getLength()) + + val.getLength() + 1; + val = der.read(); + revocationDate = (Date) val.getValue(); + len += DERWriter.definiteEncodingSize(val.getLength()) + + val.getLength() + 1; + + if (len < entry.getLength()) + { + if (version < 2) + throw new ASN1ParsingException("extra data in CRL entry"); + while (len < entry.getLength()) + { + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed Extension"); + OID extOid = (OID) der.read().getValue(); + Boolean critical = Boolean.valueOf(false); + DERValue val2 = der.read(); + if (val2.getValue() instanceof Boolean) + { + critical = (Boolean) val2.getValue(); + val2 = der.read(); + } + byte[] ext = (byte[]) val2.getValue(); + extensions.put(extOid.toString(), ext); + if (critical.booleanValue()) + critOids.add(extOid.toString()); + else + nonCritOids.add(extOid.toString()); + len += val.getEncodedLength(); + } + } + } +} diff --git a/libjava/gnu/java/security/x509/X509Certificate.java b/libjava/gnu/java/security/x509/X509Certificate.java new file mode 100644 index 0000000..bb66f2e --- /dev/null +++ b/libjava/gnu/java/security/x509/X509Certificate.java @@ -0,0 +1,693 @@ +/* X509Certificate.java -- X.509 certificate. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.x509; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.Serializable; + +import java.math.BigInteger; + +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateParsingException; + +import java.security.spec.DSAParameterSpec; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.RSAPublicKeySpec; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.BitString; +import gnu.java.security.der.DER; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; +import gnu.java.security.der.DERWriter; + +/** + * An implementation of X.509 certificates. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class X509Certificate extends java.security.cert.X509Certificate + implements Serializable +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + private static final OID ID_DSA = new OID("1.2.840.10040.4.1"); + private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3"); + private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1"); + private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2"); + private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4"); + private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5"); + + private static final OID ID_EXTENSION = new OID("2.5.29"); + private static final OID ID_KEY_USAGE = ID_EXTENSION.getChild(15); + private static final OID ID_BASIC_CONSTRAINTS = ID_EXTENSION.getChild(19); + private static final OID ID_EXT_KEY_USAGE = ID_EXTENSION.getChild(37); + + private static final int OTHER_NAME = 0; + private static final int RFC882_NAME = 1; + private static final int DNS_NAME = 2; + private static final int X400_ADDRESS = 3; + private static final int DIRECTORY_NAME = 4; + private static final int EDI_PARTY_NAME = 5; + private static final int URI = 6; + private static final int IP_ADDRESS = 7; + private static final int REGISTERED_ID = 8; + + // This object SHOULD be serialized with an instance of + // java.security.cert.Certificate.CertificateRep, thus all fields are + // transient. + + // The encoded certificate. + private transient byte[] encoded; + + // TBSCertificate part. + private transient byte[] tbsCertBytes; + private transient int version; + private transient BigInteger serialNo; + private transient OID algId; + private transient byte[] algVal; + private transient X500Principal issuer; + private transient Date notBefore; + private transient Date notAfter; + private transient X500Principal subject; + private transient PublicKey subjectKey; + private transient BitString issuerUniqueId; + private transient BitString subjectUniqueId; + private transient HashMap extensions; + private transient HashSet critOids; + private transient HashSet nonCritOids; + + private transient BitString keyUsage; + private transient int basicConstraints = -1; + + // Signature. + private transient OID sigAlgId; + private transient byte[] sigAlgVal; + private transient byte[] signature; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new X.509 certificate from the encoded data. The input + * data are expected to be the ASN.1 DER encoding of the certificate. + * + * @param encoded The encoded certificate data. + * @throws IOException If the certificate cannot be read, possibly + * from a formatting error. + * @throws CertificateException If the data read is not an X.509 + * certificate. + */ + public X509Certificate(InputStream encoded) + throws CertificateException, IOException + { + super(); + extensions = new HashMap(); + critOids = new HashSet(); + nonCritOids = new HashSet(); + try + { + parse(encoded); + } + catch (IOException ioe) + { + throw ioe; + } + catch (Exception e) + { + throw new CertificateException(e.toString()); + } + } + + // X509Certificate methods. + // ------------------------------------------------------------------------ + + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException + { + checkValidity(new Date()); + } + + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException + { + if (date.compareTo(notBefore) < 0) + throw new CertificateNotYetValidException(); + if (date.compareTo(notAfter) > 0) + throw new CertificateExpiredException(); + } + + public int getVersion() + { + return version; + } + + public BigInteger getSerialNumber() + { + return serialNo; + } + + public Principal getIssuerDN() + { + return getIssuerX500Principal(); + } + + public X500Principal getIssuerX500Principal() + { + return issuer; + } + + public Principal getSubjectDN() + { + return getSubjectX500Principal(); + } + + public X500Principal getSubjectX500Principal() + { + return subject; + } + + public Date getNotBefore() + { + return (Date) notBefore.clone(); + } + + public Date getNotAfter() + { + return (Date) notAfter.clone(); + } + + public byte[] getTBSCertificate() throws CertificateEncodingException + { + return (byte[]) tbsCertBytes.clone(); + } + + public byte[] getSignature() + { + return (byte[]) signature.clone(); + } + + public String getSigAlgName() + { + if (sigAlgId.equals(ID_DSA_WITH_SHA1)) + return "SHA1withDSA"; + if (sigAlgId.equals(ID_RSA_WITH_MD2 )) + return "MD2withRSA"; + if (sigAlgId.equals(ID_RSA_WITH_MD5 )) + return "MD5withRSA"; + if (sigAlgId.equals(ID_RSA_WITH_SHA1 )) + return "SHA1withRSA"; + return "unknown"; + // return sigAlgId.getShortName(); + } + + public String getSigAlgOID() + { + return sigAlgId.toString(); + } + + public byte[] getSigAlgParams() + { + return (byte[]) sigAlgVal.clone(); + } + + public boolean[] getIssuerUniqueID() + { + if (issuerUniqueId != null) + return issuerUniqueId.toBooleanArray(); + return null; + } + + public boolean[] getSubjectUniqueID() + { + if (subjectUniqueId != null) + return subjectUniqueId.toBooleanArray(); + return null; + } + + public boolean[] getKeyUsage() + { + if (keyUsage != null) + return keyUsage.toBooleanArray(); + return null; + } + + public List getExtendedKeyUsage() throws CertificateParsingException + { + byte[] ext = (byte[]) extensions.get("2.5.29.37"); + if (ext == null) + return null; + LinkedList usages = new LinkedList(); + try + { + DERReader der = new DERReader(new ByteArrayInputStream(ext)); + DERValue seq = der.read(); + if (!seq.isConstructed()) + throw new CertificateParsingException(); + int len = 0; + while (len < seq.getLength()) + { + DERValue oid = der.read(); + if (!(oid.getValue() instanceof OID)) + throw new CertificateParsingException(); + usages.add(oid.getValue().toString()); + len += DERWriter.definiteEncodingSize(oid.getLength()) + + oid.getLength() + 1; + } + } + catch (IOException ioe) + { + throw new CertificateParsingException(); + } + return usages; + } + + public int getBasicConstraints() + { + return basicConstraints; + } + + public Collection getSubjectAlternativeNames() + throws CertificateParsingException + { + byte[] ext = getExtensionValue("2.5.29.17"); + if (ext == null) + return null; + return getAltNames(ext); + } + + public Collection getIssuerAlternativeNames() + throws CertificateParsingException + { + byte[] ext = getExtensionValue("2.5.29.18"); + if (ext == null) + return null; + return getAltNames(ext); + } + +// X509Extension methods. + // ------------------------------------------------------------------------ + + public boolean hasUnsupportedCriticalExtension() + { + for (Iterator it = critOids.iterator(); it.hasNext(); ) + { + String oid = (String) it.next(); + if (!oid.equals("2.5.29.15") && !oid.equals("2.5.29.17") && + !oid.equals("2.5.29.18") && !oid.equals("2.5.29.19") && + !oid.equals("2.5.29.37")) + return true; + } + return false; + } + + public Set getCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(critOids); + } + + public Set getNonCriticalExtensionOIDs() + { + return Collections.unmodifiableSet(nonCritOids); + } + + public byte[] getExtensionValue(String oid) + { + byte[] ext = (byte[]) extensions.get(oid); + if (ext != null) + return (byte[]) ext.clone(); + return null; + } + + // Certificate methods. + // ------------------------------------------------------------------------ + + public byte[] getEncoded() throws CertificateEncodingException + { + return (byte[]) encoded.clone(); + } + + public void verify(PublicKey key) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + Signature sig = Signature.getInstance(sigAlgId.toString()); + doVerify(sig, key); + } + + public void verify(PublicKey key, String provider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException + { + Signature sig = Signature.getInstance(sigAlgId.toString(), provider); + doVerify(sig, key); + } + + public String toString() + { + // XXX say more than this. + return gnu.java.security.x509.X509Certificate.class.getName(); + } + + public PublicKey getPublicKey() + { + return subjectKey; + } + + public Object writeReplace() throws java.io.ObjectStreamException + { + return super.writeReplace(); + } + + // Own methods. + // ------------------------------------------------------------------------ + + /** + * Verify this certificate's signature. + */ + private void doVerify(Signature sig, PublicKey key) + throws CertificateException, InvalidKeyException, SignatureException + { + sig.initVerify(key); + sig.update(tbsCertBytes); + if (!sig.verify(signature)) + throw new CertificateException("signature not validated"); + } + + /** + * Read a GeneralNames structure. + */ + private List getAltNames(byte[] encoded) + throws CertificateParsingException + { + LinkedList names = new LinkedList(); + try + { + ByteArrayInputStream in = new ByteArrayInputStream(encoded); + DERReader der = new DERReader(in); + DERValue seq = der.read(); + if (!seq.isConstructed()) + throw new CertificateParsingException(); + int len = 0; + while (len < seq.getLength()) + { + DERValue name = der.read(); + ArrayList pair = new ArrayList(2); + Object nameVal = null; + switch (name.getTag()) + { + case RFC882_NAME: + case DNS_NAME: + case URI: + nameVal = new String((byte[]) name.getValue()); + break; + case IP_ADDRESS: + nameVal = java.net.InetAddress.getByAddress( + (byte[]) name.getValue()).getHostAddress(); + break; + case REGISTERED_ID: + nameVal = new OID((byte[]) name.getValue()); + break; + case OTHER_NAME: + case X400_ADDRESS: + case DIRECTORY_NAME: + case EDI_PARTY_NAME: + nameVal = name.getEncoded(); + break; + default: + throw new CertificateParsingException(); + } + pair.add(new Integer(name.getTag())); + pair.add(nameVal); + names.add(pair); + if (name.isConstructed()) + in.skip(name.getLength()); + len += name.getEncodedLength(); + } + } + catch (IOException ioe) + { + throw new CertificateParsingException(ioe.toString()); + } + return Collections.unmodifiableList(names); + } + + /** + * Parse a DER stream into an X.509 certificate. + * + * @param encoded The encoded bytes. + */ + private void parse(InputStream encoded) throws Exception + { + DERReader der = new DERReader(encoded); + + // Certificate ::= SEQUENCE { + DERValue cert = der.read(); + this.encoded = cert.getEncoded(); + if (!cert.isConstructed()) + throw new ASN1ParsingException("malformed Certificate"); + + // TBSCertificate ::= SEQUENCE { + DERValue tbsCert = der.read(); + if (tbsCert.getValue() != DER.CONSTRUCTED_VALUE) + throw new ASN1ParsingException("malformed TBSCertificate"); + tbsCertBytes = tbsCert.getEncoded(); + + DERValue val = der.read(); + if (val.getTagClass() == DER.CONTEXT && val.getTag() == 0) + { + // Version ::= INTEGER [0] { v1(0), v2(1), v3(2) } + version = ((BigInteger) der.read().getValue()).intValue() + 1; + val = der.read(); + } + else + { + version = 1; + } + // SerialNumber ::= INTEGER + serialNo = (BigInteger) val.getValue(); + + // AlgorithmIdentifier ::= SEQUENCE { + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed AlgorithmIdentifier"); + int certAlgLen = val.getLength(); + val = der.read(); + algId = (OID) val.getValue(); + if (certAlgLen > val.getEncodedLength()) + { + val = der.read(); + if (val == null) + algVal = null; + else + algVal = val.getEncoded(); + if (val.isConstructed()) + encoded.skip(val.getLength()); + } + + issuer = new X500Principal(encoded); + + if (!der.read().isConstructed()) + throw new ASN1ParsingException("malformed Validity"); + notBefore = (Date) der.read().getValue(); + notAfter = (Date) der.read().getValue(); + + subject = new X500Principal(encoded); + + if (!der.read().isConstructed()) + throw new ASN1ParsingException("malformed SubjectPublicKeyInfo"); + + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed AlgorithmIdentifier"); + int keyAlgLen = val.getLength(); + val = der.read(); + OID keyID = (OID) val.getValue(); + byte[] keyParams = null; + if (keyAlgLen > val.getEncodedLength()) + { + val = der.read(); + keyParams = val.getEncoded(); + if (algVal == null) + algVal = keyParams; + if (val.isConstructed()) + encoded.skip(val.getLength()); + } + val = der.read(); + byte[] keyVal = ((BitString) val.getValue()).toByteArray(); + + if (keyID.equals(ID_DSA)) + { + AlgorithmParameters params = AlgorithmParameters.getInstance("DSA"); + params.init(keyParams, "ASN.1"); + KeyFactory keyFac = KeyFactory.getInstance("DSA"); + DSAParameterSpec spec = (DSAParameterSpec) + params.getParameterSpec(DSAParameterSpec.class); + subjectKey = keyFac.generatePublic(new DSAPublicKeySpec( + (BigInteger) new DERReader(keyVal).read().getValue(), + spec.getP(), spec.getQ(), spec.getG())); + } + else if (keyID.equals(ID_RSA)) + { + KeyFactory keyFac = KeyFactory.getInstance("RSA"); + DERReader rsaKey = new DERReader(keyVal); + if (!rsaKey.read().isConstructed()) + throw new ASN1ParsingException("malformed RSAPublicKey"); + subjectKey = keyFac.generatePublic(new RSAPublicKeySpec( + (BigInteger) rsaKey.read().getValue(), + (BigInteger) rsaKey.read().getValue())); + } + else + throw new ASN1ParsingException("unknown key algorithm " + keyID); + + if (version > 1) + val = der.read(); + if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 1) + { + byte[] b = (byte[]) val.getValue(); + issuerUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF); + val = der.read(); + } + if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 2) + { + byte[] b = (byte[]) val.getValue(); + subjectUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF); + val = der.read(); + } + if (version >= 3 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 3) + { + val = der.read(); + int len = 0; + while (len < val.getLength()) + { + DERValue ext = der.read(); + OID extId = (OID) der.read().getValue(); + DERValue val2 = der.read(); + Boolean crit = Boolean.valueOf(false); + if (val2.getValue() instanceof Boolean) + { + crit = (Boolean) val2.getValue(); + val2 = der.read(); + } + byte[] extVal = (byte[]) val2.getValue(); + extensions.put(extId.toString(), extVal); + if (crit.booleanValue()) + critOids.add(extId.toString()); + else + nonCritOids.add(extId.toString()); + if (extId.equals(ID_KEY_USAGE)) + { + keyUsage = (BitString) DERReader.read(extVal).getValue(); + } + else if (extId.equals(ID_BASIC_CONSTRAINTS)) + { + DERReader bc = new DERReader(extVal); + DERValue constraints = bc.read(); + if (!constraints.isConstructed()) + throw new ASN1ParsingException("malformed BasicConstraints"); + if (constraints.getLength() > 0) + { + boolean ca = false; + int constr = -1; + val2 = bc.read(); + if (val2.getValue() instanceof Boolean) + { + ca = ((Boolean) val2.getValue()).booleanValue(); + if (constraints.getLength() > val2.getEncodedLength()) + val2 = bc.read(); + } + if (val2.getValue() instanceof BigInteger) + constr = ((BigInteger) val2.getValue()).intValue(); + basicConstraints = constr; + } + } + len += ext.getEncodedLength(); + } + } + + val = der.read(); + if (!val.isConstructed()) + throw new ASN1ParsingException("malformed AlgorithmIdentifier"); + int sigAlgLen = val.getLength(); + val = der.read(); + sigAlgId = (OID) val.getValue(); + if (sigAlgLen > val.getEncodedLength()) + { + val = der.read(); + if (val.getValue() == null) + sigAlgVal = keyParams; + else + sigAlgVal = (byte[]) val.getEncoded(); + if (val.isConstructed()) + encoded.skip(val.getLength()); + } + signature = ((BitString) der.read().getValue()).toByteArray(); + } +} |