[Webfunds-commits] java/webfunds/sox Crypto.java

Jeroen C. van Gelderen gelderen@cypherpunks.ai
Mon, 4 Sep 2000 19:16:59 -0400 (AST)


gelderen    00/09/04 19:16:59

  Modified:    webfunds/sox Crypto.java
  Log:
  Do Asymmetric crypto with Cryptix JCE. We have to include a couple of hacks
  to work around long-standing RSA bugs in Cryptix 3 which is used by fielded
  WebFunds implementations. These hacks can go as soon as those implentations
  are retired.

Revision  Changes    Path
1.50      +140 -191  java/webfunds/sox/Crypto.java

Index: Crypto.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Crypto.java,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -r1.49 -r1.50
--- Crypto.java	2000/08/24 02:02:08	1.49
+++ Crypto.java	2000/09/04 23:16:59	1.50
@@ -1,4 +1,4 @@
-/* $Id: Crypto.java,v 1.49 2000/08/24 02:02:08 gelderen Exp $
+/* $Id: Crypto.java,v 1.50 2000/09/04 23:16:59 gelderen Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * The WebFunds Development Team.  All Rights Reserved.
@@ -22,10 +22,12 @@
 import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.Security;
 import java.security.Signature;
 import java.security.SignatureException;
 import java.security.cert.Certificate;
 import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.RSAPublicKeySpec;
 import java.security.spec.RSAPrivateKeySpec;
@@ -41,15 +43,15 @@
 import javax.crypto.spec.IvParameterSpec;
 
 import cryptix.jce.provider.asn.*;
+import cryptix.jce.provider.key.RawSecretKey;
+import cryptix.jce.provider.rsa.RSAPrivateCrtKeyCryptix;
+import cryptix.jce.provider.rsa.RSAPrivateKeyCryptix;
+import cryptix.jce.provider.rsa.RSAPublicKeyImpl;
 
 import cryptix.openpgp.PGPPublicKey;
 import cryptix.openpgp.PGPSecretKey;
 import cryptix.openpgp.algorithm.PGPRSA;
 
-import cryptix.provider.key.RawSecretKey;
-import cryptix.provider.rsa.RawRSAPrivateKey;
-import cryptix.provider.rsa.RawRSAPublicKey;
-
 import webfunds.utils.Hex;
 
 
@@ -58,7 +60,7 @@
  *
  * Centralized crypto methods. Currently being overhauled.
  *
- * @version $Revision: 1.49 $
+ * @version $Revision: 1.50 $
  */
 public final class Crypto
 {
@@ -73,22 +75,18 @@
 //............................................................................
 
     static {
-        java.security.Security.addProvider(
-            new cryptix.jce.provider.CryptixCrypto() );
-
-        //'till JCE porting is complete, Cryptix3 MUST come AFTER CryptixCrypto
-        java.security.Security.addProvider(new cryptix.provider.Cryptix());
+        Security.addProvider( new cryptix.jce.provider.CryptixCrypto() );
     }
     
     
-    private static String 
+    private static final String 
         CIPHER_ALGORITHM = "DESede",
         CIPHER_TRANSFORM = "DESede/CBC/PKCS#5",
         MD_ALGORITHM     = "SHA";
 
-    public static int cipher_keylen = 24;
-    public static String pk_alg = "RSA";
-    public static String sig_alg = "MD5/RSA";
+    public static final int cipher_keylen = 24;
+    public static final String pk_alg = "RSA";
+    public static final String sig_alg = "MD5withRSA";
 
 
     public static SecureRandom sr;
@@ -140,7 +138,7 @@
         logDebug("generateKeys(...)");
         try {
             if (keyGen == null)
-                keyGen = KeyPairGenerator.getInstance(pk_alg, "CryptixCrypto");
+                keyGen = KeyPairGenerator.getInstance("RSA", "CryptixCrypto");
 
             keyGen.initialize(bits, getSecureRandom());
             KeyPair keyPair = keyGen.generateKeyPair();
@@ -148,7 +146,7 @@
             return keyPair;
 
         } catch(NoSuchAlgorithmException ex) {
-            throw new ProviderException(pk_alg+" Algorithm missing, " + ex);
+            throw new ProviderException("RSA algorithm missing, " + ex);
 
         } catch(NoSuchProviderException e) {
             throw new ProviderException();
@@ -188,7 +186,7 @@
      */
     public static PublicKey getPublicKeyFromCert(Certificate cert)
     {
-        logDebug(cert.toString());
+        logDebug("getPublicKeyFromCert returns:\n" + cert.toString());
         return toCryptixKey( cert.getPublicKey() );
     }
 
@@ -414,49 +412,11 @@
         throws KeyException
     {
         try {
-            java.security.Cipher rsa = 
-                java.security.Cipher.getInstance(pk_alg);
-            pk = toCryptixKey(pk);
-            rsa.initEncrypt(pk);
+            Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS#1", "CryptixCrypto");
+            rsa.init(Cipher.ENCRYPT_MODE, pk);
 
             byte[] keyData = key.getEncoded();
-            byte[] rsa_pkt = new byte[rsa.getPlaintextBlockSize()];
-        
-            //
-            // Generate the padding
-            //
-            int padlen = rsa_pkt.length - keyData.length - 3;
-            if (padlen < 8)
-                throw new InternalError("No room for padding");
-        
-            byte[] padding = new byte[padlen];
-            SecureRandom sr = getSecureRandom();
-            sr.nextBytes(padding);
-        
-            // Find a random non-zero number ...
-            byte rnd = padding[0];
-            for (int i = padlen-1; rnd == 0 && i >= 0; i--)
-                rnd = padding[i];
-
-            // ... with which to replace zero bytes in the padding
-            // (since this padding must be non-zero)
-            for (int i = padlen-1; i >= 0; i--)
-                if (padding[i] == 0)
-                    padding[i] = rnd;
-        
-            //
-            // Generate the PKCS#1 packet
-            //
-            rsa_pkt[0] = 0;
-            rsa_pkt[1] = 2;
-            System.arraycopy(padding, 0, rsa_pkt, 2, padlen);
-            rsa_pkt[padlen+2] = 0;
-            System.arraycopy(keyData, 0, rsa_pkt, padlen+3, keyData.length);
-        
-            //
-            // Encrypt the data
-            //
-            byte[] encHdr = rsa.crypt(rsa_pkt);
+            byte[] encHdr = rsa.doFinal(keyData);
             byte[] encData = encrypt(key, data, offset, len);
         
             //
@@ -467,9 +427,9 @@
             System.arraycopy(encData, 0, retval, encHdr.length, encData.length);
             return retval;
 
-        } catch (NoSuchAlgorithmException e) {
-            throw new ProviderException(
-                "Algorithm (" + pk_alg + ") not found (" + e.getMessage()+")");
+        } catch(Exception e) {
+            e.printStackTrace();
+            throw new ProviderException("NY");
         }
     }
 
@@ -490,7 +450,7 @@
                                     byte[] data, int offset, int len)
         throws KeyException
     {
-      return pk_encrypt(generateKey(), pk, data, offset, len);
+        return pk_encrypt(generateKey(), pk, data, offset, len);
     }
 
    /**
@@ -506,7 +466,7 @@
     public static byte[] pk_encrypt(Key key, PublicKey pk, byte[] data)
         throws KeyException
     {
-      return pk_encrypt(key, pk, data, 0, data.length);
+        return pk_encrypt(key, pk, data, 0, data.length);
     }
 
 
@@ -524,50 +484,15 @@
                                      byte[] data, int offset, int len)
         throws KeyException
     {
-        pk = translateKey(pk);
         try {
-            //
-            // Decrypt the key
-            //
-            java.security.Cipher rsa = java.security.Cipher.getInstance(pk_alg);
-            rsa.initDecrypt(pk);
-
-            int pktlen = rsa.getCiphertextBlockSize() + 1;
-        
-            System.err.println("pktlen: "+pktlen+", data.length: "+data.length);
-        
-            byte[] rsa_pkt = rsa.crypt(data, offset, pktlen);
-            len -= pktlen;
-            offset += pktlen;
-        
-            //
-            // Unpack the PKCS#1 packet
-            //
-            if (rsa_pkt[0] != 0)
-                throw new KeyException(
-                    "Bad start of PKCS1 packet in decrypted data");
-
-            if (rsa_pkt[1] != 2)
-                throw new KeyException("Bad block type in decrypted data");
-
-            // Data starts after first zero octet
-            int pad_start = 2;
-            while(pad_start < rsa_pkt.length && rsa_pkt[pad_start++] != 0)
-                ;
-
-            if (pad_start == rsa_pkt.length)
-                throw new KeyException("Bad PKCS#1 packet in decrypted data");
-
-            byte[] key = new byte[rsa_pkt.length - pad_start];
-            if (key.length != cipher_keylen)
-                throw new KeyException("Unexpected size of PKCS#1 data field");
-
-            System.arraycopy(rsa_pkt, pad_start, key, 0, key.length);
+            Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS#1", "CryptixCrypto");
+            rsa.init(Cipher.DECRYPT_MODE, pk);
+            int pktlen = getKeyLength(pk);
+            byte[] key = rsa.doFinal(data, offset, pktlen);
             return new RawSecretKey(CIPHER_ALGORITHM, key);
 
-        } catch (NoSuchAlgorithmException e) {
-            throw new ProviderException(
-                pk_alg+" Algorithm not found ("+e.getMessage()+")");
+        } catch(Exception e) {
+            throw new ProviderException("NY");
         }
     }
 
@@ -589,20 +514,22 @@
                                          byte[] data, int offset, int len)
         throws KeyException
     {
-        pk = translateKey(pk);
-        try {
-            // Only using cipher to get packet length ...
-            java.security.Cipher rsa = java.security.Cipher.getInstance(pk_alg);
-            rsa.initDecrypt(pk);
-            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()+")");
-        }
+        int pktlen = getKeyLength(pk);
+        return decrypt(key, data, offset+pktlen, len-pktlen);
     }
 
 
+    private static int getKeyLength(PrivateKey key) {
+        
+        if( !(key instanceof RSAPrivateKey) )
+            throw new IllegalArgumentException(
+                "!(key instanceof RSAPrivateKey)");
+
+        RSAPrivateKey privKey = (RSAPrivateKey)key;
+        return (privKey.getModulus().bitLength()+7) / 8;
+    }
+
+
     /**
      * Decrypt data, and return the original plaintext
      *
@@ -672,14 +599,18 @@
      */
     public static byte[] sign(PrivateKey key, byte[] data) throws KeyException {
         try {
-            Signature sig = Signature.getInstance(sig_alg); 
+            Signature sig = Signature.getInstance(sig_alg, "CryptixCrypto"); 
             sig.initSign(key);
             sig.update(data);
-            return encodeSignature(sig.sign());
+            byte[] sigBytes = sig.sign();
+            return encodeSignature(sigBytes);
 
         } catch (NoSuchAlgorithmException e) {
             throw new ProviderException(
-                pk_alg+" Algorithm not found ("+e.getMessage()+")");
+                sig_alg+" Algorithm not found ("+e.getMessage()+")");
+
+        } catch (NoSuchProviderException e) {
+            throw new ProviderException("Provider CryptixCrypto not found.");
 
         } catch (SignatureException e) {
             throw new InternalError("sign() failed ("+e.getMessage()+")");
@@ -712,7 +643,7 @@
             return sig.verify(decodeSignature(sigX509));
 
         } catch (NoSuchAlgorithmException e) {
-            throw new ProviderException(pk_alg+" Algorithm not found ("+
+            throw new ProviderException(sig_alg+" Algorithm not found ("+
                                         e.getMessage()+")");
         } catch (SignatureException e) {
             e.printStackTrace();
@@ -758,22 +689,50 @@
     }
 
 
-   /**
-    * Generate a "fingerprint" from an X509 key
-    * The fingerprint is simply an message digest of the encoded X509Key.
-    *
-    * @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)
-    */
-    public static byte[] fingerprint(PublicKey key) throws InvalidKeyException {
-        key = translateKey(key);
-        return digest(key.getEncoded());
+    public static byte[] fingerprint(PublicKey key)
+        throws InvalidKeyException
+    {
+        if( !(key instanceof RSAPublicKey) )
+            throw new IllegalArgumentException(
+                "!(key instanceof RSAPublicKey)");
+
+        RSAPublicKey pubKey = (RSAPublicKey)key;
+
+        try {
+            BigInteger e = pubKey.getPublicExponent();
+            int eBitLen = e.bitLength();
+
+            BigInteger n = pubKey.getModulus();
+            int nBitLen = n.bitLength();
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            baos.write(eBitLen >>> 8);
+            baos.write(eBitLen & 0xFF);
+            baos.write(getMagnitude(e));
+
+            baos.write(nBitLen >>> 8);
+            baos.write(nBitLen & 0xFF);
+            baos.write(getMagnitude(n));
+
+            return digest(baos.toByteArray());
+        } catch(IOException e) {
+            throw new InternalError("PANIC");
+        }
     }
 
 
+    public static byte[] getMagnitude(BigInteger x) {
+        byte[] y = x.toByteArray();    // including leading 0(s).
+        int i = 0;
+        for ( ; y[i] == 0 && i < y.length - 1; i++)
+            ;
 
+        byte[] result = new byte[y.length - i]; // excluding leading 0(s).
+        System.arraycopy(y, i, result, 0, y.length - i);
+        return result;
+    }
+
+
 ////////////////////////////////////////////////////////////////////////
 // Methods for encoding and decoding X509 signatures
 ////////////////////////////////////////////////////////////////////////
@@ -880,7 +839,7 @@
             AsnInteger n = (AsnInteger)seq.get(0);
             AsnInteger e = (AsnInteger)seq.get(1);
 
-            return new RawRSAPublicKey(
+            return new RSAPublicKeyImpl(
                 n.toBigInteger(), 
                 e.toBigInteger());
 
@@ -899,7 +858,6 @@
      * @return the encoded key (as a byte array)
      */
     public static byte[] encodePublicKey(PublicKey key) {
-        key = translateKey(key);
 
         /*
          * An RSA public key is defined as:
@@ -910,15 +868,15 @@
          * }
          */
 
-        if( !(key instanceof RawRSAPublicKey) )
+        if( !(key instanceof RSAPublicKey) )
             throw new InternalError("Unexpected object");
 
-        RawRSAPublicKey pub = (RawRSAPublicKey)key;
+        RSAPublicKey pub = (RSAPublicKey)key;
 
         try {
             AsnSequence seq = new AsnSequence(
                 new AsnInteger( pub.getModulus() ),
-                new AsnInteger( pub.getExponent() ) );
+                new AsnInteger( pub.getPublicExponent() ) );
 
             AsnOutputStream dos = new AsnOutputStream();
             dos.write(seq);
@@ -985,10 +943,14 @@
              * Note: Cryptix RSA private key constructor doesn't bother
              *       with values n, e, dmp1 or dmq1.
              */
-            return new RawRSAPrivateKey(
-                d.toBigInteger(), 
+            return new RSAPrivateCrtKeyCryptix(
+                n.toBigInteger(), 
+                e.toBigInteger(),
+                d.toBigInteger(),
                 p.toBigInteger(),
                 q.toBigInteger(),
+                dmp1.toBigInteger(),
+                dmq1.toBigInteger(),
                 u.toBigInteger() );
 
         } catch(ClassCastException e) {
@@ -1007,7 +969,6 @@
      */
     public static byte[] encodePrivateKey(PrivateKey key)
     {
-        key = translateKey(key);
         // An RSA private key is defined as:
         //
         // SEQUENCE {
@@ -1021,12 +982,11 @@
         //   Integer U (IQMP)
         // }
         //
+
+        if( !(key instanceof RSAPrivateCrtKey) )
+            throw new InternalError("Unexpected object");
 
-        // Convert to a Cryptix key
-        if (!(key instanceof cryptix.provider.rsa.RawRSAPrivateKey))
-                    throw new InternalError("Unexpected object");
-        cryptix.provider.rsa.RawRSAPrivateKey priv;
-        priv = (cryptix.provider.rsa.RawRSAPrivateKey)key;
+        RSAPrivateCrtKey priv = (RSAPrivateCrtKey)key;
 
         try {
             //
@@ -1036,9 +996,9 @@
             //
             BigInteger one = BigInteger.valueOf(1L);
             BigInteger m = priv.getModulus();
-            BigInteger p = priv.getP();
-            BigInteger q = priv.getQ();
-            BigInteger d = priv.getExponent();
+            BigInteger p = priv.getPrimeP();
+            BigInteger q = priv.getPrimeQ();
+            BigInteger d = priv.getPrivateExponent();
             BigInteger phi = p.subtract(one).multiply(q.subtract(one));
             BigInteger e = d.modInverse(phi);
             BigInteger dmp1 = d.mod(p.subtract(one));
@@ -1052,7 +1012,7 @@
             vals[4] = new AsnInteger(q);
             vals[5] = new AsnInteger(dmp1);
             vals[6] = new AsnInteger(dmq1);
-            vals[7] = new AsnInteger( priv.getInverseOfQModP() );
+            vals[7] = new AsnInteger( priv.getCrtCoefficient() );
             AsnSequence seq = new AsnSequence(vals);
 
             AsnOutputStream os = new AsnOutputStream();
@@ -1064,37 +1024,11 @@
         }
     }
 
-    private static PrivateKey translateKey(PrivateKey key) {
-        if( key.getFormat().equals("Cryptix") ) {
-            RSAPrivateCrtKey rsapk = (RSAPrivateCrtKey)key;
-            return new RawRSAPrivateKey(
-                rsapk.getPrivateExponent(),
-                rsapk.getPrimeP(),
-                rsapk.getPrimeQ(),
-                rsapk.getCrtCoefficient() );
-        }
-
-        return key;
-    }
-
-
-    private static PublicKey translateKey(PublicKey key) {
-        return toCryptixKey(key);
-    }
-
 
-    /**
-     * Convert the given key to a Cryptix' RawRSAPublicKey.
-     *
-     * <p>This is a no-op when the given key already is RawRSAPublicKey.</p>
-     */
     public static PublicKey toCryptixKey(PublicKey pk) {
 
         // XXX: move out of Certificate.java. Can go for JCE 1.2
 
-        /*
-         * Don't do conversion when the key is a Cryptix RAW-encoded key.
-         1G*/
         if( pk.getFormat().equals("RAW") )
             return pk;
 
@@ -1102,7 +1036,7 @@
             RSAPublicKey rsapk = (RSAPublicKey)pk;
             BigInteger n = rsapk.getModulus();
             BigInteger e = rsapk.getPublicExponent();
-            return new RawRSAPublicKey(n, e);   
+            return new RSAPublicKeyImpl(n, e);   
         }
 
         if( pk.getFormat().equals("OpenPGP") ) {
@@ -1112,7 +1046,7 @@
                 RSAPublicKeySpec rsapubspec = pgprsa.getRSAPublicKeySpec();
                 BigInteger n = rsapubspec.getModulus();
                 BigInteger e = rsapubspec.getPublicExponent();
-                return new RawRSAPublicKey(n, e);   
+                return new RSAPublicKeyImpl(n, e);   
             } else {
                 return pk;
             }
@@ -1126,7 +1060,7 @@
             seq = (AsnSequence)is.read();
             AsnInteger n = (AsnInteger)seq.get(0);
             AsnInteger e = (AsnInteger)seq.get(1);
-            return new RawRSAPublicKey(n.toBigInteger(), e.toBigInteger());
+            return new RSAPublicKeyImpl(n.toBigInteger(), e.toBigInteger());
         } catch(IOException e) {
             return null;
         }
@@ -1143,7 +1077,7 @@
                 RSAPrivateKeySpec rsaprivspec = pgprsa.getRSAPrivateKeySpec();
                 BigInteger n = rsaprivspec.getModulus();
                 BigInteger d = rsaprivspec.getPrivateExponent();
-                return new RawRSAPrivateKey(n, d);   
+                return new RSAPrivateKeyCryptix(n, d);   
             } else {
                 return pk;
             }
@@ -1151,7 +1085,20 @@
         } else {
             return null;
         }
+    }
+
+
+    public static PublicKey toCryptixJCEKey(PublicKey pk) {
 
+        logDebug("toCryptixJCEKey: pk instanceof " + pk.getClass());
+
+        if( pk instanceof cryptix.jce.provider.rsa.RSAPublicKeyCryptix )
+            return pk;
+
+        if( pk instanceof cryptix.jce.provider.rsa.RSAPublicKeyImpl )
+            return pk;
+
+        throw new RuntimeException();
     }
 
 
@@ -1159,20 +1106,22 @@
 // Test code
 ////////////////////////////////////////////////////////////////////////
 
-   /**
-     * Test code
-     */
     public static void main(String args[])
         throws Exception
     {
-        args = null;
-        Key key = Crypto.generateKey();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        baos.write("x".getBytes());
-        baos.write(encrypt(key, "stuff".getBytes(), 1, 3));
-        baos.write("x".getBytes());
-        byte[] encData = baos.toByteArray();
-        String s = new String(decrypt(key, encData, 1, encData.length-2));
-        System.out.println("Decrypt="+s);
+        KeyPair kp = generateKeys(1024);
+
+        byte[] pt = new byte[16];
+        System.out.println("pt: " + Hex.data2hex(pt));
+
+        byte[] ct = pk_encrypt(kp.getPublic(), pt, 0, pt.length);
+
+        System.out.println("ct: " + Hex.data2hex(ct));
+
+        byte[] protoPt = pk_decrypt(kp.getPrivate(), ct, 0, ct.length);
+        System.out.println("ppt: " + Hex.data2hex(protoPt));
+
+        for(int i=0; i<protoPt.length; i++)
+            if( protoPt[i] != 0 ) System.out.println("Oops!");
     }
 }