[Webfunds-commits] java/webfunds/x509 AsnAlgorithmId.java AsnBitString.java AsnInputStream.java AsnInteger.java AsnNull.java AsnObject.java AsnObjectId.java AsnOutputStream.java AsnSequence.java DerException.java SubInputStream.java Test.java

Jeroen C. van Gelderen gelderen@cypherpunks.ai
Tue, 18 Jul 2000 22:35:43 -0400 (AST)


gelderen    00/07/18 22:35:43

  Modified:    webfunds/sox Crypto.java
  Added:       webfunds/x509 AsnAlgorithmId.java AsnBitString.java
                        AsnInputStream.java AsnInteger.java AsnNull.java
                        AsnObject.java AsnObjectId.java
                        AsnOutputStream.java AsnSequence.java
                        DerException.java SubInputStream.java Test.java
  Log:
  

Revision  Changes    Path
1.37      +233 -263  java/webfunds/sox/Crypto.java

Index: Crypto.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Crypto.java,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- Crypto.java	2000/07/14 00:17:16	1.36
+++ Crypto.java	2000/07/19 02:35:41	1.37
@@ -1,4 +1,4 @@
-/* $Id: Crypto.java,v 1.36 2000/07/14 00:17:16 gelderen Exp $
+/* $Id: Crypto.java,v 1.37 2000/07/19 02:35:41 gelderen Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * The WebFunds Development Team.  All Rights Reserved.
@@ -28,39 +28,31 @@
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
-// XXX: Sun X.509 classes we shouldn't be using
-import sun.security.util.BigInt;        // done
-import sun.security.util.DerOutputStream;
-import sun.security.util.DerValue;
-import sun.security.x509.AlgorithmId;
-import sun.security.x509.CertException; // done
-import sun.security.x509.X509Cert;      // done
-import sun.security.x509.X509Key;
-
 import cryptix.provider.key.RawSecretKey;
+import cryptix.provider.rsa.RawRSAPrivateKey;
+import cryptix.provider.rsa.RawRSAPublicKey;
 
 import webfunds.utils.Hex;
+import webfunds.x509.AsnAlgorithmId;
+import webfunds.x509.AsnInputStream;
+import webfunds.x509.AsnObject;
+import webfunds.x509.AsnObjectId;
+import webfunds.x509.AsnOutputStream;
+import webfunds.x509.AsnBitString;
+import webfunds.x509.AsnInteger;
+import webfunds.x509.AsnSequence;
+import webfunds.x509.X509Cert;
 
 
 /**
+ * XXX: Do Not Touch This File!!!
+ *
  * Centralized crypto methods. Currently being overhauled.
  *
- * @version $Revision: 1.36 $
+ * @version $Revision: 1.37 $
  */
-public class Crypto
+public final class Crypto
 {
-    /**
-     * This function pulls in the hotlava provider.
-     * We expect the Cryptix provider to be installed already.
-     *
-     * The hotlava provider basically maps additional names to
-     * the existing Cryptix algorithm classes. This is neccessary
-     * because the sun.* classes use hardwired algorithm names
-     * which are not supported by an out-of-the-box Cryptix setup.
-     *
-     * This section requires the hotlava.crypto.Provider to be
-     * present. Otherwise the program won't load.
-     */
     static
     {
         java.security.Security.addProvider(new cryptix.provider.Cryptix());
@@ -78,7 +70,6 @@
     public static String cipher_padding = "PKCS#5";
     public static String pk_alg = "RSA";
     public static int pk_strength = 384; // Testing
-// aw shit, can't just arbitrarily change MD5 to SHA - see AlgorithmId.java
     public static String sig_alg = "MD5/RSA";
     public static int cert_validity = 365 * 24 * 60 * 60;  // a year
     public static boolean quiet = true;
@@ -157,7 +148,7 @@
         try {
             cert.verify(key);
             return true;
-        } catch (CertException e) {
+        } catch (SecurityException e) { // XXX: do CertException again
             return false ;
         }
     }
@@ -171,7 +162,7 @@
     public static PublicKey getPublicKeyFromCert(X509Cert cert)
     {
         try {
-            X509Key xkey = (X509Key)cert.getPublicKey();
+            PublicKey xkey = cert.getPublicKey();
             return decodePublicKey(keyData(xkey));
         } catch (InvalidKeyException e) {
             throw new IllegalArgumentException(
@@ -252,8 +243,10 @@
    /**
      * Create a message digest (as a byte array) from
      * data (as a byte array)
-     * The digest uses the algorithm specified in crypto.message-digest.algorithm,
+     * The digest uses the algorithm specified in 
+     * crypto.message-digest.algorithm,
      * and if not defined, defaults to the md_alg algorithm.
+     *
      * @param data the data to be digested
      * @param offset the offset into the array of the start of the data
      * @param len the length of the data
@@ -276,8 +269,10 @@
    /**
      * Create a message digest (as a byte array) from
      * data (as a byte array)
-     * The digest uses the algorithm specified in crypto.message-digest.algorithm,
+     * The digest uses the algorithm specified in 
+     * crypto.message-digest.algorithm,
      * and if not defined, defaults to the md_alg algorithm.
+     *
      * @param data the data to be digested
      * @return the message digest (as a byte array)
      */
@@ -309,7 +304,8 @@
      * @param len the length of the data
      * @return the secure checksum (as a byte array)
      */
-    public static byte[] checksum(String password, byte[] data, int offset, int len)
+    public static byte[] checksum(String password, 
+                                  byte[] data, int offset, int len)
     {
         MessageDigest md;
         try {
@@ -392,7 +388,8 @@
         throw new IllegalArgumentException("Invalid key ("+e.getMessage()+")");
       }
       catch (NoSuchAlgorithmException e) {
-        throw new ProviderException("Symmetric Algorithm not found ("+e.getMessage()+")");
+        throw new ProviderException(
+            "Symmetric Algorithm not found ("+e.getMessage()+")");
       }
     }
     
@@ -408,7 +405,8 @@
      * @param len the length of the data with the <i>data</i> array
      * @return the encrypted data
      */
-    public static byte[] encrypt(String password, byte[] data, int offset, int len)
+    public static byte[] encrypt(String password, 
+                                 byte[] data, int offset, int len)
     {
       return encrypt(password2key(password), data, offset, len);
     }
@@ -468,7 +466,8 @@
             int zippedLen = cipher.doFinal(data, offset, len, data, 0);
             
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ByteArrayInputStream bais = new ByteArrayInputStream(data, 0, zippedLen);
+            ByteArrayInputStream bais = 
+                new ByteArrayInputStream(data, 0, zippedLen);
             
             GZIPInputStream gzis = new GZIPInputStream(bais);
             byte[] buffer = new byte[4096];
@@ -505,7 +504,8 @@
      * @param len the length of the data with the <i>data</i> array
      * @return the decrypted data
      */
-    public static byte[] decrypt(String password, byte[] data, int offset, int len)
+    public static byte[] decrypt(String password, 
+                                 byte[] data, int offset, int len)
         throws KeyException
     {
       return decrypt(password2key(password), data, offset, len);
@@ -554,10 +554,11 @@
      * @param offset the offset within <i>data</i> where the data starts
      * @param len the length of the data with the <i>data</i> array
      * @return the encrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the public key, such as
-     * it not being in the correct format.
+     * @exception KeyException a problem has occurred with the public key, 
+     * such as it not being in the correct format.
      */
-    public static byte[] pk_encrypt(Key key, PublicKey pk, byte[] data, int offset, int len)
+    public static byte[] pk_encrypt(Key key, PublicKey pk, 
+                                    byte[] data, int offset, int len)
         throws KeyException
     {
       try {
@@ -634,10 +635,11 @@
      * @param offset the offset within <i>data</i> where the data starts
      * @param len the length of the data with the <i>data</i> array
      * @return the encrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the public key, such as
-     * it not being in the correct format.
+     * @exception KeyException a problem has occurred with the public key, 
+     * such as it not being in the correct format.
      */
-    public static byte[] pk_encrypt(PublicKey pk, byte[] data, int offset, int len)
+    public static byte[] pk_encrypt(PublicKey pk, 
+                                    byte[] data, int offset, int len)
         throws KeyException
     {
       return pk_encrypt(generateKey(), pk, data, offset, len);
@@ -650,8 +652,8 @@
      * @param pk the PublicKey with which to encrypt the symmetric key
      * @param data the data to be encrypted (as a byte array)
      * @return the encrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the public key, such as
-     * it not being in the correct format.
+     * @exception KeyException a problem has occurred with the public key, 
+     * such as it not being in the correct format.
      */
     public static byte[] pk_encrypt(Key key, PublicKey pk, byte[] data)
         throws KeyException
@@ -666,8 +668,8 @@
      * @param pk the PublicKey with which to encrypt the symmetric key
      * @param data the data to be encrypted (as a byte array)
      * @return the encrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the public key, such as
-     * it not being in the correct format.
+     * @exception KeyException a problem has occurred with the public key, 
+     * such as it not being in the correct format.
      */
     public static byte[] pk_encrypt(PublicKey pk, byte[] data)
         throws KeyException
@@ -698,7 +700,7 @@
 
         int pktlen = rsa.getCiphertextBlockSize() + 1;
         
-        System.err.println("pktlen: " + pktlen + ", data.length: " + data.length);
+        System.err.println("pktlen: " +pktlen+", data.length: "+data.length);
         
         byte[] rsa_pkt = rsa.crypt(data, offset, pktlen);
         len -= pktlen;
@@ -728,7 +730,8 @@
         return new RawSecretKey(cipher_alg, key);
 
       } catch (NoSuchAlgorithmException e) {
-        throw new ProviderException(pk_alg+" Algorithm not found ("+e.getMessage()+")");
+        throw new ProviderException(
+            pk_alg+" Algorithm not found ("+e.getMessage()+")");
       }
     }
 
@@ -737,7 +740,8 @@
      * This method is pretty much the same as the decrypt method,
      * except that the PKCS#1 header is skipped.
      *
-     * @param pk the PrivateKey (required to know the header length - no time consuming asymmetric decryption is done)
+     * @param pk the PrivateKey (required to know the header length 
+     *        - no time consuming asymmetric decryption is done)
      * @param key a symmetric Key object with which to decrypt the data
      * @param data the data to be decrypted (as a byte array)
      * @param offset the offset within <i>data</i> where the pk-packet starts
@@ -745,7 +749,8 @@
      * @return the decrypted Key from the data header
      * @exception KeyException a problem has occurred with the private key
      */
-    public static byte[] pk_decrypt_data(PrivateKey pk, Key key, byte[] data, int offset, int len)
+    public static byte[] pk_decrypt_data(PrivateKey pk, Key key, 
+                                         byte[] data, int offset, int len)
         throws KeyException
     {
       try {
@@ -755,7 +760,8 @@
         int pktlen = rsa.getCiphertextBlockSize();
         return decrypt(key, data, offset+pktlen, len-pktlen);
       } catch (NoSuchAlgorithmException e) {
-        throw new ProviderException(pk_alg+" Algorithm not found ("+e.getMessage()+")");
+        throw new ProviderException(
+            pk_alg+" Algorithm not found ("+e.getMessage()+")");
       }
     }
 
@@ -768,10 +774,11 @@
      * @param offset the offset within <i>data</i> where the pk-packet starts
      * @param len the length of the pk-packet with the <i>data</i> array
      * @return the decrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the private key, such as
-     * the password being incorrect.
+     * @exception KeyException a problem has occurred with the private key, 
+     *            such as the password being incorrect.
      */
-    public static byte[] pk_decrypt(PrivateKey pk, byte[] data, int offset, int len)
+    public static byte[] pk_decrypt(PrivateKey pk, 
+                                    byte[] data, int offset, int len)
         throws KeyException
     {
       Key key = pk_decrypt_key(pk, data, offset, len);
@@ -798,7 +805,8 @@
      * This method is pretty much the same as the decrypt method,
      * except that the PKCS#1 header is skipped.
      *
-     * @param pk the PrivateKey (required to know the header length - no time consuming asymmetric decryption is done)
+     * @param pk the PrivateKey (required to know the header length - 
+     *        no time consuming asymmetric decryption is done)
      * @param key a symmetric Key object with which to decrypt the data
      * @param data the data to be decrypted (as a byte array)
      * @return the decrypted Key from the data header
@@ -816,8 +824,8 @@
      * @param pk a PrivateKey with which to decrypt the data
      * @param data the data to be decrypted (as a byte array)
      * @return the decrypted data (as a byte array)
-     * @exception KeyException a problem has occurred with the private key, such as
-     * the password being incorrect.
+     * @exception KeyException a problem has occurred with the private key, 
+     *            such as the password being incorrect.
      */
     public static byte[] pk_decrypt(PrivateKey pk, byte[] data)
         throws KeyException
@@ -837,8 +845,8 @@
      * @param key a PrivateKey with which to sign the data
      * @param data the data being signed (as a byte array)
      * @return the binary X509 signature
-     * @exception KeyException a problem has occurred with the private key, such as
-     * the password being incorrect.
+     * @exception KeyException a problem has occurred with the private key, 
+     * such as the password being incorrect.
      */
     public static byte[] sign(PrivateKey key, byte[] data)
         throws KeyException
@@ -847,7 +855,8 @@
       try {
         sig = java.security.Signature.getInstance(sig_alg); 
       } catch (NoSuchAlgorithmException e) {
-        throw new ProviderException(pk_alg+" Algorithm not found ("+e.getMessage()+")");
+        throw new ProviderException(
+            pk_alg+" Algorithm not found ("+e.getMessage()+")");
       }
 
       // Initialise the object with the private key
@@ -911,83 +920,34 @@
 // Methods for extracting data from keys and certificates
 ////////////////////////////////////////////////////////////////////////
 
-   /**
-    * Extract the algorithm id from an X509Key object
-    *
-    * @param key An X509Key object, from which to extract the algorithm id
-    * @return the AlgorithmId object from within the key
-    * @exception InvalidKeyException the X509Key is invalid (i.e. incorrectly formatted)
-    */
-    public static AlgorithmId algorithmId(X509Key key)
+    /**
+     * Extract the key data (the bitstring) from the X509Key.
+     */
+    private static byte[] keyData(PublicKey key)
         throws InvalidKeyException
     {
-        //
-        // The X509Key is defined as:
-        // SEQUENCE {
-        //   AlgorithId id
-        //   BitString key
-        // }
-      
-        //
-        //        Extract the key data (the bitstring) from the X509Key
-        //
-        try {
-            DerValue dv = new DerValue(key.getEncoded());
-            
-            // Ensure we have a SEQUENCE
-            if (dv.tag != DerValue.tag_Sequence)
-              throw new InvalidKeyException("invalid key format");
-            
-            AlgorithmId algorithmId = AlgorithmId.parse(dv.data.getDerValue());
-            byte[] keydata = dv.data.getBitString();
-            
-            // Ensure no excess data
-            if (dv.data.available() != 0)
-                throw new InvalidKeyException("excess key data");
-            
-            return algorithmId;
-        } catch (IOException e) {
-            e = null;
-            throw new InvalidKeyException("Could not parse encoded key data");
-        }
-    }
+        System.out.println("Fiets!");
 
+        /*
+         * The X509Key is defined as:
+         *
+         * SEQUENCE
+         *   AlgorithId id
+         *   BitString key
+         */
 
-   /**
-    * Extract the key data from an X509Key object
-    *
-    * @param key An X509Key object, from which to extract the public key data
-    * @return the raw key data from within the key
-    * @exception InvalidKeyException the X509Key is invalid (i.e. incorrectly formatted)
-    */
-    public static byte[] keyData(X509Key key)
-        throws InvalidKeyException
-    {
-        //
-        // The X509Key is defined as:
-        // SEQUENCE {
-        //   AlgorithId id
-        //   BitString key
-        // }
-
-        //
-        //        Extract the key data (the bitstring) from the X509Key
-        //
         try {
-              DerValue dv = new DerValue(key.getEncoded());
-            
-              // Ensure we have a SEQUENCE
-              if (dv.tag != DerValue.tag_Sequence)
-                  throw new InvalidKeyException("invalid key format");
-            
-              AlgorithmId algorithmId = AlgorithmId.parse(dv.data.getDerValue());
-              byte[] keydata = dv.data.getBitString();
-            
-              // Ensure no excess data
-              if (dv.data.available() != 0)
-                  throw new InvalidKeyException("excess key data");
-            
-              return keydata;
+            AsnInputStream is = new AsnInputStream(key.getEncoded());
+            AsnSequence seq = (AsnSequence)is.read();
+            if( (is.available()!=0) || (seq.size() != 2) )
+                throw new InvalidKeyException("Excess data detected.");
+
+            //AsnAlgorithmId oid  = (AsnAlgorithmId)seq.get(0);
+            AsnBitString   bits = (AsnBitString)seq.get(1);
+            return bits.toByteArray();
+
+        } catch(ClassCastException e) {
+            throw new InvalidKeyException("Unexpected ASN.1 type detected.");
         } catch (IOException e) {
             throw new InvalidKeyException("Could not parse encoded key data");
         }
@@ -1000,7 +960,8 @@
     *
     * @param key An X509Key object, from which to extract the public key data
     * @return the key "fingerprint"
-    * @exception InvalidKeyException the X509Key is invalid (i.e. incorrectly formatted)
+    * @exception InvalidKeyException the X509Key is invalid 
+    *            (i.e. incorrectly formatted)
     */
     public static byte[] fingerprint(PublicKey key)
         throws InvalidKeyException
@@ -1019,36 +980,34 @@
     *
     * @param data The X509 signature packet containing the raw signature
     * @return the raw signature data
-    * @exception SignatureException the signature packet is invalid (i.e. incorrectly formatted)
+    * @exception SignatureException the signature packet is invalid 
+    *            (i.e. incorrectly formatted)
     */
     public static byte[] decodeSignature(byte[] data)
         throws SignatureException
     {
-        //
-        // An X509 signature is defined as:
-        //
-        // SEQUENCE {
-        //   AlgorithmId id
-        //   BitString signature
-        // }
-        //
+        System.out.println("Automobiel!");
+        /*
+         * An X509 signature is defined as:
+         *
+         * SEQUENCE
+         *   AlgorithmId id
+         *   BitString signature
+         */
+        
         try {
-            DerValue dv = new DerValue(data);
-            
-            // Ensure we have a SEQUENCE
-            if (dv.tag != DerValue.tag_Sequence)
-              throw new SignatureException("invalid key format");
-            
-            /* AlgorithmId algorithmId = */
-            AlgorithmId.parse(dv.data.getDerValue());
-            byte[] sig = dv.data.getBitString();
-
-            // Ensure no excess data
-            if (dv.data.available() != 0)
-              throw new SignatureException("excess raw key data");
-            
-            return sig;
-        } catch (IOException e) {
+            AsnInputStream is = new AsnInputStream(data);
+            AsnSequence seq = (AsnSequence)is.read();
+            if( (is.available()!=0) || (seq.size() != 2) )
+                throw new SignatureException("Excess data detected.");
+
+            //AsnAlgorithmId oid  = (AsnAlgorithmId)seq.get(0);
+            AsnBitString   bits = (AsnBitString)seq.get(1);
+            return bits.toByteArray();
+
+        } catch(ClassCastException e) {
+            throw new SignatureException("Unexpected ASN.1 type detected.");
+        } catch(IOException e) {
             throw new SignatureException("Could not parse encoded signature");
         }
     }
@@ -1072,21 +1031,21 @@
         //
 
         try {
-            DerOutputStream dos = new DerOutputStream();
-            (AlgorithmId.get("MD5withRSA")).emit(dos);
-            dos.putBitString(sig);
-            return new DerValue(DerValue.tag_Sequence,
-                                dos.toByteArray()).toByteArray();
-            // return dos.toByteArray();
-        } catch (NoSuchAlgorithmException e) {
-            throw new ProviderException("Algorithm not found: "+e.getMessage());
+            int[] OID_MD5_WITH_RSA = {  1, 2, 840, 113549, 1, 1, 4 };
+            AsnSequence seq = new AsnSequence(
+                new AsnAlgorithmId( new AsnObjectId(OID_MD5_WITH_RSA) ),
+                new AsnBitString(sig) );
+
+            AsnOutputStream os = new AsnOutputStream();
+            os.write(seq);
+            return os.toByteArray();
+
         } catch (IOException e) {
             throw new InternalError("Unexpected IOException");
         }
     }
 
 
-
 ////////////////////////////////////////////////////////////////////////
 // Methods for encoding and decoding public keys
 ////////////////////////////////////////////////////////////////////////
@@ -1096,39 +1055,40 @@
      *
      * @param data The encoded data containing the public key
      * @return a PublicKey object, derived from the data
-     * @exception InvalidKeyException the encoded public key is invalid (i.e. incorrectly formatted)
+     * @exception InvalidKeyException the encoded public key is invalid 
+     *            (i.e. incorrectly formatted)
      */
     public static PublicKey decodePublicKey(byte[] data)
         throws InvalidKeyException
     {
-        // An RSA public key is defined as:
-        //
-        // SEQUENCE {
-        //   Integer Modulus
-        //   Integer Exponent
-        // }
-        //
+        /*
+         * An RSA public key is defined as:
+         *
+         * SEQUENCE {
+         *   INTEGER Modulus
+         *   INTEGER Exponent
+         */
         try {
-            DerValue dv = new DerValue(data);
-            
-            // Ensure we have a SEQUENCE
-            if (dv.tag != DerValue.tag_Sequence)
-                throw new InvalidKeyException("invalid key format");
-            
-            java.math.BigInteger n = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger e = dv.data.getInteger().toBigInteger();
-            
-            // Ensure no excess data
-            if (dv.data.available() != 0)
-                throw new InvalidKeyException("excess raw key data");
-            
-            // Convert from a Cryptix key
-            return (PublicKey)new cryptix.provider.rsa.RawRSAPublicKey(n, e);
-        } catch (IOException e) {
-            throw new InvalidKeyException("Could not parse encoded key data");
+            AsnInputStream is = new AsnInputStream(data);
+            AsnSequence seq = (AsnSequence)is.read();
+            if( (is.available()!=0) || (seq.size() != 2) )
+                throw new InvalidKeyException("Excess data detected.");
+
+            AsnInteger n = (AsnInteger)seq.get(0);
+            AsnInteger e = (AsnInteger)seq.get(1);
+
+            return new RawRSAPublicKey(
+                n.toBigInteger(), 
+                e.toBigInteger());
+
+        } catch(ClassCastException e) {
+            throw new InvalidKeyException("Unexpected ASN.1 type detected.");
+        } catch(IOException e) {
+            throw new InvalidKeyException("Could not parse encoded key data.");
         }
     }
 
+
     /**
      * Encode an RSA PublicKey object
      *
@@ -1137,26 +1097,29 @@
      */
     public static byte[] encodePublicKey(PublicKey key)
     {
-        // An RSA public key is defined as:
-        //
-        // SEQUENCE {
-        //   Integer Modulus
-        //   Integer Exponent
-        // }
-        //
+        /*
+         * An RSA public key is defined as:
+         *       
+         * SEQUENCE {
+         *   INTEGER Modulus
+         *   INTEGER Exponent
+         * }
+         */
 
-        // Convert to a Cryptix key
-        if (!(key instanceof cryptix.provider.rsa.RawRSAPublicKey))
+        if( !(key instanceof RawRSAPublicKey) )
             throw new InternalError("Unexpected object");
-        cryptix.provider.rsa.RawRSAPublicKey pub;
-        pub = (cryptix.provider.rsa.RawRSAPublicKey)key;
+
+        RawRSAPublicKey pub = (RawRSAPublicKey)key;
 
         try {
-            DerOutputStream dos = new DerOutputStream();
-            dos.putInteger(new BigInt(pub.getModulus()));
-            dos.putInteger(new BigInt(pub.getExponent()));
-            return new DerValue(DerValue.tag_Sequence,
-                                dos.toByteArray()).toByteArray();
+            AsnSequence seq = new AsnSequence(
+                new AsnInteger( pub.getModulus() ),
+                new AsnInteger( pub.getExponent() ) );
+
+            AsnOutputStream dos = new AsnOutputStream();
+            dos.write(seq);
+            return dos.toByteArray();
+
         } catch (IOException e) {
             throw new InternalError("Unexpected IOException");
         }
@@ -1172,59 +1135,62 @@
      *
      * @param data The encoded data containing the private key
      * @return a PrivateKey object, derived from the data
-     * @exception InvalidKeyException the encoded private key is invalid (i.e. incorrectly formatted)
+     * @exception InvalidKeyException the encoded private key is invalid 
+     *            (i.e. incorrectly formatted)
      */
     public static PrivateKey decodePrivateKey(byte[] data)
-                throws InvalidKeyException
+        throws InvalidKeyException
     {
-        // An RSA private key is defined as:
-        //
-        // SEQUENCE {
-        //   Integer N
-        //   Integer E
-        //   Integer D
-        //   Integer P
-        //   Integer Q
-        //   Integer DMP1
-        //   Integer DMQ1
-        //   Integer U (IQMP)
-        // }
-        //
+        /*
+         * An RSA private key is defined as:
+         *
+         * SEQUENCE
+         *   INTEGER N
+         *   INTEGER E
+         *   INTEGER D
+         *   INTEGER P
+         *   INTEGER Q
+         *   INTEGER DMP1
+         *   INTEGER DMQ1
+         *   INTEGER U (IQMP)
+         */
 
         try
         {
-            DerValue dv = new DerValue(data);
-            
-            // Ensure we have a SEQUENCE
-            if (dv.tag != DerValue.tag_Sequence)
-                throw new InvalidKeyException("invalid key format");
-            
-            java.math.BigInteger n = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger e = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger d = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger p = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger q = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger dmp1 = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger dmq1 = dv.data.getInteger().toBigInteger();
-            java.math.BigInteger u = dv.data.getInteger().toBigInteger();
-            
-            // Ensure no excess data
-            if (dv.data.available() != 0)
-                throw new InvalidKeyException("excess raw key data");
-            
-            // Convert from a Cryptix key
-            PrivateKey k;
-            k = new cryptix.provider.rsa.RawRSAPrivateKey(d, p, q, u);
-
-            // Note - Cryptix RSA private key constructor doesn't bother
-            // with values n, e, dmp1 or dmq1
+            AsnInputStream is = new AsnInputStream(data);
+            AsnSequence seq = (AsnSequence)is.read();
+            if( (is.available()!=0) || (seq.size() != 8) )
+                throw new InvalidKeyException("Excess data detected.");
+
+            /*
+             * NOTE: not all of these are used but the casts make sure
+             *       that all ASN objects are indeed INTEGERS.
+             */
+            AsnInteger n    = (AsnInteger)seq.get(0);
+            AsnInteger e    = (AsnInteger)seq.get(1);
+            AsnInteger d    = (AsnInteger)seq.get(2);
+            AsnInteger p    = (AsnInteger)seq.get(3);
+            AsnInteger q    = (AsnInteger)seq.get(4);
+            AsnInteger dmp1 = (AsnInteger)seq.get(5);
+            AsnInteger dmq1 = (AsnInteger)seq.get(6);
+            AsnInteger u    = (AsnInteger)seq.get(7);
+
+            /* 
+             * Convert to a Cryptix key.
+             *
+             * Note: Cryptix RSA private key constructor doesn't bother
+             *       with values n, e, dmp1 or dmq1.
+             */
+            return new RawRSAPrivateKey(
+                d.toBigInteger(), 
+                p.toBigInteger(),
+                q.toBigInteger(),
+                u.toBigInteger() );
 
-            return k;
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace();
-            throw new InvalidKeyException("Could not parse encoded key data");
+        } catch(ClassCastException e) {
+            throw new InvalidKeyException("Unexpected ASN.1 type detected.");
+        } catch (IOException e) {
+            throw new InvalidKeyException("Could not parse encoded key data.");
         }
     }
 
@@ -1264,6 +1230,7 @@
             // e, dmp1, dmq1 and iqmp)
             //
             BigInteger one = BigInteger.valueOf(1L);
+            BigInteger m = priv.getModulus();
             BigInteger p = priv.getP();
             BigInteger q = priv.getQ();
             BigInteger d = priv.getExponent();
@@ -1272,18 +1239,21 @@
             BigInteger dmp1 = d.mod(p.subtract(one));
             BigInteger dmq1 = d.mod(q.subtract(one));
 
-            DerOutputStream dos = new DerOutputStream();
-            dos.putInteger(new BigInt(priv.getModulus()));
-            dos.putInteger(new BigInt(e));
-            dos.putInteger(new BigInt(d));
-            dos.putInteger(new BigInt(p));
-            dos.putInteger(new BigInt(q));
-            dos.putInteger(new BigInt(dmp1));
-            dos.putInteger(new BigInt(dmq1));
-            dos.putInteger(new BigInt(priv.getInverseOfQModP()));
+            AsnInteger[] vals = new AsnInteger[8];
+            vals[0] = new AsnInteger(m);
+            vals[1] = new AsnInteger(e);
+            vals[2] = new AsnInteger(d);
+            vals[3] = new AsnInteger(p);
+            vals[4] = new AsnInteger(q);
+            vals[5] = new AsnInteger(dmp1);
+            vals[6] = new AsnInteger(dmq1);
+            vals[7] = new AsnInteger( priv.getInverseOfQModP() );
+            AsnSequence seq = new AsnSequence(vals);
+
+            AsnOutputStream os = new AsnOutputStream();
+            os.write(seq);
+            return os.toByteArray();
 
-            return new DerValue(DerValue.tag_Sequence,
-                                dos.toByteArray()).toByteArray();
         } catch (IOException e) {
             throw new InternalError("Unexpected IOException");
         }



1.1                  java/webfunds/x509/AsnAlgorithmId.java

Index: AsnAlgorithmId.java
===================================================================
/* $Id: AsnAlgorithmId.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnAlgorithmId extends AsnObject
{
    private final AsnSequence val;


    public AsnAlgorithmId(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_SEQUENCE);
        this.val = new AsnSequence(is);
    }


    public AsnAlgorithmId(AsnObjectId oid) {
        super(AsnObject.TAG_SEQUENCE);
        this.val = new AsnSequence(oid, new AsnNull());
    }


    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
        this.val.encodePayload(os);
    }


    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        return this.val.getEncodedLengthOfPayload(os);
    }
}



1.1                  java/webfunds/x509/AsnBitString.java

Index: AsnBitString.java
===================================================================
/* $Id: AsnBitString.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;
import java.math.BigInteger;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnBitString extends AsnObject
{
    private final byte[] val;


    /*package*/ AsnBitString(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_BITSTRING);

        int len = is.readLength() - 1; // subtract 1 'unused' byte
        if(len < 0)
            throw new DerException("Negative length.");

        // verify multiple of 8 bits
        byte unused = is.readByte();
        if(unused != 0)
            throw new DerException("Length not a multiple of 8.");
        
        this.val = is.readBytes(len);
    }


    public AsnBitString(byte[] value) {
        super(AsnObject.TAG_BITSTRING);

        this.val = (byte[])(value.clone());
    }


    public byte[] toByteArray() {
        return (byte[])this.val.clone();
    }


    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
        os.writeByte((byte)0x00); // no. of unsed bits (always 0)
        os.writeBytes(this.val);
    }


    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        return this.val.length+1; // plus 1 byte for 'unused bits field'
    }
}



1.1                  java/webfunds/x509/AsnInputStream.java

Index: AsnInputStream.java
===================================================================
/* $Id: AsnInputStream.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnInputStream {

// Instance variables
//............................................................................

    private final InputStream is;


//............................................................................

    public AsnInputStream(byte[] data) {
        this.is = new ByteArrayInputStream(data);
    }


    public AsnInputStream(InputStream is) {
        this.is = is;
    }


//............................................................................

    public AsnObject read() throws IOException {
        int tag = this.is.read();
        if(tag == -1)
            throw new IOException("End of stream.");

        System.out.println("tag: " +tag);

        switch(tag) {
        case AsnObject.TAG_OBJECT_ID:
            return new AsnObjectId(this);
        case AsnObject.TAG_BITSTRING:
            return new AsnBitString(this);
        case AsnObject.TAG_INTEGER:
            return new AsnInteger(this);
        case AsnObject.TAG_NULL:
            return new AsnNull(this);
        case AsnObject.TAG_SEQUENCE:
            return new AsnSequence(this);
        default:
            throw new IOException("Unknown ASN object type.");
        }
    }


//............................................................................

    public int available() throws IOException {
        return this.is.available();
    }


    /*package*/ int readLength() throws IOException {

        int b = this.is.read();
        if( b == -1 ) 
            throw new IOException("Unexpected end of stream.");

        // short form
        if( b <= 127 ) 
            return b;

        // strip of high bit
        b = b&0x7F;

        // long form
        if( b > 4 )
            throw new IOException("Length too big.");

        int t, res = 0;
        while( b-- > 0 ) {
            if( (t=this.is.read()) == -1 )
                throw new IOException("Unexpected end of stream.");
            res = (res<<8) | t;
        }

        if( res < 0 )
            throw new IOException("Negative length.");

        return res;
    }


    /*package*/ byte readByte() throws IOException {
        return this.readBytes(1)[0];
    }


    /*package*/ byte[] readBytes(int todo) throws IOException {
        byte[] res = new byte[todo];
        int done, off = 0;
        while( todo > 0 ) {
            if( (done = this.is.read(res, off, todo)) == -1 )
                throw new IOException("EOF");
            todo -= done;
            off  += done;
        }
        return res;
    }


    /*package*/ AsnInputStream getSubStream(int len) {
        return new AsnInputStream( new SubInputStream(this.is, len) );
    }
}



1.1                  java/webfunds/x509/AsnInteger.java

Index: AsnInteger.java
===================================================================
/* $Id: AsnInteger.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;
import java.math.BigInteger;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnInteger extends AsnObject
{

//............................................................................

    private final BigInteger val;


//............................................................................

    /*package*/ AsnInteger(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_INTEGER);

        int len = is.readLength();
        byte[] data = is.readBytes(len);
        this.val = new BigInteger(data);
    }


    public AsnInteger(BigInteger value) {
        super(AsnObject.TAG_INTEGER);

        this.val = value;
    }


//............................................................................

    public BigInteger toBigInteger() {
        return this.val;
    }


//............................................................................

    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
        os.writeBytes( this.val.toByteArray() );
    }


    /** 
     * Returns no. of bytes encodePayload will write out when called on
     * the given AsnOutputStream.
     */
    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        return this.val.toByteArray().length;
    }
}



1.1                  java/webfunds/x509/AsnNull.java

Index: AsnNull.java
===================================================================
/* $Id: AsnNull.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnNull extends AsnObject
{
    public AsnNull(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_NULL);
        int len = is.readLength();
    }


    public AsnNull() {
        super(AsnObject.TAG_NULL);
    }


    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
    }


    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        return 0;
    }
}



1.1                  java/webfunds/x509/AsnObject.java

Index: AsnObject.java
===================================================================
/* $Id: AsnObject.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;


/**
 * Base class of all ASN objects. Provides type safety and common functionality.
 * AsnObjects can encode themselves to AsnOutputStreams. All AsnObjects are
 * immutable.
 *
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@cryptix.org)
 */
public abstract class AsnObject {

    /*package*/ static final byte
        TAG_MASK      = 0x1F,
        TAG_INTEGER   = 0x02,
        TAG_BITSTRING = 0x03,
        TAG_NULL      = 0x05,
        TAG_OBJECT_ID = 0x06,
        TAG_SEQUENCE  = 0x10 | 0x20;


    private final byte tag;


    protected AsnObject(byte tag) {
        this.tag = tag;
    }


    /*package*/ final void encode(AsnOutputStream os) throws IOException {
        os.writeType( this.tag );
        os.writeLength( this.getEncodedLengthOfPayload(os) );
        this.encodePayload(os);
    }


    /*package*/ final int getEncodedLength(AsnOutputStream os) {
        int len = this.getEncodedLengthOfPayload(os);
        len += os.getLengthOfLength(len);
        len += 1; // tag
        return len;
    }


    /**
     * Write out this object's payload. Called upon by AsnObject.encode(...)
     * after it has written the type tag and length field.
     */
    protected abstract void encodePayload(AsnOutputStream os) 
    throws IOException;


    /** 
     * Returns no. of bytes encodePayload will write out when called on
     * the given AsnOutputStream.
     *
     * This is used by constructed types to determine the aggregrated
     * length of their components.
     *
     * The stream argument is included for future proofing. Different
     * streams may support different encodings for the length field.
     * This obviously doesn't apply to DER :-)
     */
    protected abstract int getEncodedLengthOfPayload(AsnOutputStream os);
}



1.1                  java/webfunds/x509/AsnObjectId.java

Index: AsnObjectId.java
===================================================================
/* $Id: AsnObjectId.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnObjectId extends AsnObject
{
    private final int[] components;


    public AsnObjectId(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_OBJECT_ID);

        int len = is.readLength();
        if( len < 2 )
            throw new IOException("Invalid OBJECT_ID.");

        is.readBytes(len);
        this.components = null; // XXX: but this makes WebFunds work :-)
    }


    public AsnObjectId(int[] components) {
        super(AsnObject.TAG_OBJECT_ID);

        if( components.length < 2 )
            throw new IllegalArgumentException("Less than 2 components.");

        this.components = (int[])components.clone();
    }


    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
        os.writeByte( (byte)(40*components[0] + components[1]) );
        for(int i=2; i<components.length; i++)
            this.writeComponent( os, components[i] );
    }


    /** 
     * Returns no. of bytes encodePayload will write out when called on
     * the given AsnOutputStream. Payload length does NOT include length.
     */
    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        int len = 1; // first two are special
        for(int i=2; i<this.components.length; i++)
            len += getEncodedComponentLen( this.components[i] );

        return len;
    }


    private void writeComponent(AsnOutputStream os, int c) throws IOException {
        int len = getEncodedComponentLen(c);
        for(int i=len-1; i>0; i--)
            os.writeByte( (byte)((c>>>i*7) | 0x80) );

        os.writeByte( (byte)(c&0x7F) );
    }


    private static int getEncodedComponentLen(int c) {
        if( c < 0 )
            throw new IllegalArgumentException("c: < 0");
        else if( c <= 0x7F )
            return 1;
        else if( c <= 0x3FFF )
            return 2;
        else if( c <= 0x1FFFFF )
            return 3;
        else if( c <= 0xFFFFFFF )
            return 4;
        else
            return 5;
    }
}



1.1                  java/webfunds/x509/AsnOutputStream.java

Index: AsnOutputStream.java
===================================================================
/* $Id: AsnOutputStream.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnOutputStream {

// Instance variables
//............................................................................

    private final OutputStream os;


// Constructors and java.lang.Object overrides
//............................................................................

    public AsnOutputStream() {
        this.os = new ByteArrayOutputStream();
    }


    public AsnOutputStream(OutputStream os) {
        this.os = os;
    }


// Public interface
//............................................................................

    public void close() throws IOException {
        this.os.flush();
        this.os.close();
    }


    public void flush() throws IOException {
        this.os.flush();
    }


    public void write(AsnObject obj) throws IOException {
        obj.encode(this);
    }


    public byte[] toByteArray() {
        if( !(this.os instanceof ByteArrayOutputStream) )
            throw new IllegalStateException("Not layered on BAOS.");

        ByteArrayOutputStream baos = (ByteArrayOutputStream)this.os;
        return baos.toByteArray();
    }


// AsnObject.encode(...) callbacks and helpers
//............................................................................

    /**
     * Returns the number of bytes writeLength() will write when called 
     * with the given 'len'.
     */
    /*package*/ int getLengthOfLength(int len) {
        if( len <= 127 )
            return 1;
        else if( len <= 0xFF )
            return 2;
        else if( len <= 0xFFFF )
            return 3;
        else if( len <= 0xFFFFFF )
            return 4;
        else
            return 5;
    }


    /*package*/ void writeByte(byte b) throws IOException {
        this.os.write(b&0xFF);
    }


    /*package*/ void writeBytes(byte[] data) throws IOException {
        this.os.write(data);
    }


    /*package*/ void writeLength(int len) throws IOException {

        if(len < 0 ) 
            throw new IllegalArgumentException("len: < 0");

        // shortcut for short form
        if(len <= 127) {
            this.os.write( (char)len );
            return;
        }

        // long form
        int lenOfLenData = getLengthOfLength(len) - 1;
        this.os.write( (byte)(lenOfLenData | 0x80) );
        while( lenOfLenData-- > 0 )
            this.os.write( (byte)(len >>> (lenOfLenData*8)) );
    }


    /*package*/ void writeType(byte type) throws IOException {
        this.os.write(type);
    }
}



1.1                  java/webfunds/x509/AsnSequence.java

Index: AsnSequence.java
===================================================================
/* $Id: AsnSequence.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;
import java.util.Vector;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class AsnSequence extends AsnObject
{
    private final AsnObject[] vals;


    /*package*/ AsnSequence(AsnInputStream is) throws IOException {
        super(AsnObject.TAG_SEQUENCE);

        int len = is.readLength();
        System.out.println("   " + len);

        AsnInputStream sub_is = is.getSubStream( len );
        Vector vec = new Vector(3);
        while( sub_is.available() > 0 ) {
            System.out.println("   yeah!");
            vec.addElement( sub_is.read() );
        }
        vec.copyInto(this.vals = new AsnObject[ vec.size() ]);
    }


    public AsnSequence(AsnObject[] vals) {
        super(AsnObject.TAG_SEQUENCE);

        this.vals = (AsnObject[])vals.clone();
    }


    public AsnSequence(AsnObject a, AsnObject b) {
        super(AsnObject.TAG_SEQUENCE);
        AsnObject[] objs = new AsnObject[2];
        objs[0] = a;
        objs[1] = b;
        this.vals = objs;
    }


    public AsnObject get(int index) {
        return this.vals[index];
    }


    public int size() {
        return this.vals.length;
    }


    /** Write out payload. */
    protected void encodePayload(AsnOutputStream os) throws IOException {
        for(int i=0; i<this.vals.length; i++)
            os.write(this.vals[i]);
    }


    protected int getEncodedLengthOfPayload(AsnOutputStream os) {
        int len = 0;
        for(int i=0; i<this.vals.length; i++)
            len += this.vals[i].getEncodedLength(os);

        return len;
    }
}



1.1                  java/webfunds/x509/DerException.java

Index: DerException.java
===================================================================
/* $Id: DerException.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.IOException;


/**
 * Thrown for all DER encoding and decoding errors.
 *
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class DerException extends IOException {
    public DerException(String msg) {
        super(msg);
    }
}



1.1                  java/webfunds/x509/SubInputStream.java

Index: SubInputStream.java
===================================================================
/* $Id: SubInputStream.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.InputStream;
import java.io.IOException;


/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class SubInputStream extends InputStream {

// Instance variables
//............................................................................

    private int len;


    private final InputStream is;


//............................................................................

    public SubInputStream(InputStream is, int len) {

        if( len < 0 )
            throw new IllegalArgumentException("len: < 0");

        this.is = is;
        this.len = len;
    }

//............................................................................

    public int available() throws IOException {
        return (this.len > 0) ? this.is.available() : 0;
    }

    public int read() throws IOException {
        return (this.len-- <= 0) ? -1 : this.is.read();
    }
}



1.1                  java/webfunds/x509/Test.java

Index: Test.java
===================================================================
/* $Id: Test.java,v 1.1 2000/07/19 02:35:41 gelderen Exp $
 *
 * Copyright (c) 2000 Systemics Inc. on behalf of
 * The WebFunds Development Team. All rights reserved.
 */

package webfunds.x509;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;

import webfunds.utils.Hex;

/**
 * @version $Revision: 1.1 $
 * @author  Jeroen C. van Gelderen (gelderen@webfunds.org)
 */
public final class Test {
    public static void main(String[] argv) {

        testBitString();
        testInteger();
        testSequence();
        testObjectId();
    }


    private static void test() {
        try {
            byte[] sig = new byte[128];
            for(int i=0; i<sig.length; i++) sig[i] = (byte)170;

            int[] OID_MD5_WITH_RSA = {  1, 2, 840, 113549, 1, 1, 4 };
            AsnSequence seq = new AsnSequence(
                new AsnAlgorithmId( new AsnObjectId(OID_MD5_WITH_RSA) ),
                new AsnBitString(sig) );

            AsnOutputStream os = new AsnOutputStream();
            os.write(seq);
            byte[] result = os.toByteArray();

            System.out.println( "N sigbytes:" + Hex.data2hex(result) );

        } catch(IOException e) {
            e.printStackTrace();
        }
    }


    private static void testInteger() {
        try {
            byte[] INT_0 = { 0x02, 0x01, 0x00 };

            AsnInteger i = new AsnInteger(BigInteger.valueOf(0));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            AsnOutputStream dos = new AsnOutputStream(baos);
            dos.write(i);
            dos.close();
            byte[] encoded = baos.toByteArray();
            System.out.println( Hex.data2hex(encoded) );

            AsnInputStream is = new AsnInputStream(encoded);
            AsnInteger res = (AsnInteger)is.read();
            System.out.println( res.toBigInteger() );
        } catch(IOException e) {
            e.printStackTrace();
        }
    }


    private static void testBitString() {
        try {
            byte[] BS = { 0x6e, 0x5d, (byte)0xc0 };
            AsnBitString bs = new AsnBitString(BS);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            AsnOutputStream dos = new AsnOutputStream(baos);
            dos.write(bs);
            dos.close();

            byte[] encoded = baos.toByteArray();
            System.out.println( Hex.data2hex(encoded) );

            AsnInputStream is = new AsnInputStream(encoded);
            AsnBitString res = (AsnBitString)is.read();
            System.out.println( Hex.data2hex(res.toByteArray()) );

        } catch(IOException e) {
            e.printStackTrace();
        }
    }


    private static void testObjectId() {
        try {
            int[] vals = { 1, 2, 840, 113549 };
            AsnObjectId oid = new AsnObjectId(vals);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            AsnOutputStream dos = new AsnOutputStream(baos);
            dos.write(oid);
            dos.close();
            System.out.println(
                webfunds.utils.Hex.data2hex(baos.toByteArray()) );
            System.out.println(oid.getEncodedLength(dos));

        } catch(IOException e) {
            e.printStackTrace();
        }
    }


    private static void testSequence() {
        try {
            byte[] BS = { 0x6e, 0x5d, (byte)0xc0 };
            AsnBitString bs = new AsnBitString(BS);

            bs = new AsnBitString(new byte[300]);

            AsnInteger i = new AsnInteger(BigInteger.valueOf(-129));

            AsnObject[] objs = new AsnObject[3];
            objs[0] = bs;
            objs[1] = i;
            objs[2] = i;

            AsnSequence t = new AsnSequence(objs);
            objs[0] = t;
            objs[1] = i;
            objs[2] = bs;

            AsnSequence seq = new AsnSequence(objs);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            AsnOutputStream dos = new AsnOutputStream(baos);
            dos.write(seq);
            dos.close();

            byte[] encoded = baos.toByteArray();
            System.out.println( Hex.data2hex(encoded) );

            AsnInputStream is = new AsnInputStream(encoded);
            AsnSequence res = (AsnSequence)is.read();
            System.out.println( (res) );

            baos = new ByteArrayOutputStream();
            dos = new AsnOutputStream(baos);
            dos.write(res);
            dos.close();

            byte[] encoded2 = baos.toByteArray();
            System.out.println( Hex.data2hex(encoded2) );

            if(encoded.length != encoded2.length)
                throw new RuntimeException();

            for(int j=0; j<encoded.length; j++)
                if(encoded[j] != encoded2[j])
                    throw new RuntimeException();

        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}