Changeset 981b708


Ignore:
Timestamp:
Feb 9, 2016 8:48:23 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
6ab5b84
Parents:
651c1b6
Message:

Crypto: Use new internal key generation instead of calling
out to keytool; save CRL for new su3 amd family keys
Allow su3 bulksign for xml files (news)

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/crypto/CertUtil.java

    r651c1b6 r981b708  
    9898     *  Does NOT close the stream. Throws on all errors.
    9999     *
    100      *  @since 0.9.24, pulled out of saveCert()
    101      */
    102     private static void exportCert(Certificate cert, OutputStream out)
     100     *  @since 0.9.24, pulled out of saveCert(), public since 0.9.25
     101     */
     102    public static void exportCert(Certificate cert, OutputStream out)
    103103                                                throws IOException, CertificateEncodingException {
    104104        // Get the encoded form which is suitable for exporting
     
    366366     *  @since 0.9.25
    367367     */
    368     private static void exportCRL(X509CRL crl, OutputStream out)
     368    public static void exportCRL(X509CRL crl, OutputStream out)
    369369                                                throws IOException, CRLException {
    370370        byte[] buf = crl.getEncoded();
  • core/java/src/net/i2p/crypto/KeyStoreUtil.java

    r651c1b6 r981b708  
    1010import java.security.KeyStore;
    1111import java.security.PrivateKey;
     12import java.security.PublicKey;
    1213import java.security.cert.Certificate;
    1314import java.security.cert.CertificateExpiredException;
    1415import java.security.cert.CertificateNotYetValidException;
    1516import java.security.cert.X509Certificate;
     17import java.security.cert.X509CRL;
    1618import java.util.ArrayList;
     19import java.util.Collections;
    1720import java.util.Enumeration;
     21import java.util.EnumSet;
    1822import java.util.List;
    1923import java.util.Locale;
     
    431435     *  Create a keypair and store it in the keystore at ks, creating it if necessary.
    432436     *
     437     *  For new code, the createKeys() with the SigType argument is recommended over this one,
     438     *  as it throws exceptions, and returns the certificate and CRL.
     439     *
    433440     *  Warning, may take a long time.
    434441     *
     
    447454     */
    448455    public static boolean createKeys(File ks, String ksPW, String alias, String cname, String ou,
     456                                     int validDays, String keyAlg, int keySize, String keyPW) {
     457        boolean useKeytool = I2PAppContext.getGlobalContext().getBooleanProperty("crypto.useExternalKeytool");
     458        if (useKeytool) {
     459            return createKeysCLI(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
     460        } else {
     461            try {
     462                createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
     463                return true;
     464            } catch (GeneralSecurityException gse) {
     465                error("Create keys error", gse);
     466                return false;
     467            } catch (IOException ioe) {
     468                error("Create keys error", ioe);
     469                return false;
     470            }
     471        }
     472    }
     473
     474    /**
     475     *  New way - Native Java, does not call out to keytool.
     476     *  Create a keypair and store it in the keystore at ks, creating it if necessary.
     477     *
     478     *  This returns the public key, private key, certificate, and CRL in an array.
     479     *  All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
     480     *  The private key and selfsigned cert are stored in the keystore.
     481     *  The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
     482     *  The public key certificate may be stored separately with
     483     *  CertUtil.saveCert() if desired.
     484     *  The CRL is not stored by this method, store it with
     485     *  CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
     486     *
     487     *  Throws on all errors.
     488     *  Warning, may take a long time.
     489     *
     490     *  @param ks path to the keystore
     491     *  @param ksPW the keystore password
     492     *  @param alias the name of the key
     493     *  @param cname e.g. randomstuff.console.i2p.net
     494     *  @param ou e.g. console
     495     *  @param validDays e.g. 3652 (10 years)
     496     *  @param keyAlg e.g. DSA , RSA, EC
     497     *  @param keySize e.g. 1024
     498     *  @param keyPW the key password, must be at least 6 characters
     499     *  @return all you need:
     500     *      rv[0] is a Java PublicKey
     501     *      rv[1] is a Java PrivateKey
     502     *      rv[2] is a Java X509Certificate
     503     *      rv[3] is a Java X509CRL
     504     *  @since 0.9.25
     505     */
     506    public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
     507                                            int validDays, String keyAlg, int keySize, String keyPW)
     508                                                throws GeneralSecurityException, IOException {
     509        String algoName = getSigAlg(keySize, keyAlg);
     510        SigType type = null;
     511        for (SigType t : EnumSet.allOf(SigType.class)) {
     512            if (t.getAlgorithmName().equals(algoName)) {
     513                type = t;
     514                break;
     515            }
     516        }
     517        if (type == null)
     518            throw new GeneralSecurityException("Unsupported algorithm/size: " + keyAlg + '/' + keySize);
     519        return createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, type, keyPW);
     520    }
     521
     522    /**
     523     *  New way - Native Java, does not call out to keytool.
     524     *  Create a keypair and store it in the keystore at ks, creating it if necessary.
     525     *
     526     *  This returns the public key, private key, certificate, and CRL in an array.
     527     *  All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
     528     *  The private key and selfsigned cert are stored in the keystore.
     529     *  The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
     530     *  The public key certificate may be stored separately with
     531     *  CertUtil.saveCert() if desired.
     532     *  The CRL is not stored by this method, store it with
     533     *  CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
     534     *
     535     *  Throws on all errors.
     536     *  Warning, may take a long time.
     537     *
     538     *  @param ks path to the keystore
     539     *  @param ksPW the keystore password
     540     *  @param alias the name of the key
     541     *  @param cname e.g. randomstuff.console.i2p.net
     542     *  @param ou e.g. console
     543     *  @param validDays e.g. 3652 (10 years)
     544     *  @param keyAlg e.g. DSA , RSA, EC
     545     *  @param keySize e.g. 1024
     546     *  @param keyPW the key password, must be at least 6 characters
     547     *  @return all you need:
     548     *      rv[0] is a Java PublicKey
     549     *      rv[1] is a Java PrivateKey
     550     *      rv[2] is a Java X509Certificate
     551     *      rv[3] is a Java X509CRL
     552     *  @since 0.9.25
     553     */
     554    public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
     555                                            int validDays, SigType type, String keyPW)
     556                                                throws GeneralSecurityException, IOException {
     557        Object[] rv = SelfSignedGenerator.generate(cname, ou, "XX", "I2P Anonymous Network", "XX", "XX", validDays, type);
     558        PublicKey jpub = (PublicKey) rv[0];
     559        PrivateKey jpriv = (PrivateKey) rv[1];
     560        X509Certificate cert = (X509Certificate) rv[2];
     561        X509CRL crl = (X509CRL) rv[3];
     562        List<X509Certificate> certs = Collections.singletonList(cert);
     563        storePrivateKey(ks, ksPW, alias, keyPW, jpriv, certs);
     564        return rv;
     565    }
     566
     567    /**
     568     *  OLD way - keytool
     569     *  Create a keypair and store it in the keystore at ks, creating it if necessary.
     570     *
     571     *  Warning, may take a long time.
     572     *
     573     *  @param ks path to the keystore
     574     *  @param ksPW the keystore password
     575     *  @param alias the name of the key
     576     *  @param cname e.g. randomstuff.console.i2p.net
     577     *  @param ou e.g. console
     578     *  @param validDays e.g. 3652 (10 years)
     579     *  @param keyAlg e.g. DSA , RSA, EC
     580     *  @param keySize e.g. 1024
     581     *  @param keyPW the key password, must be at least 6 characters
     582     *
     583     *  @return success
     584     *  @since 0.8.3, consolidated from RouterConsoleRunner and SSLClientListenerRunner in 0.9.9
     585     */
     586    private static boolean createKeysCLI(File ks, String ksPW, String alias, String cname, String ou,
    449587                                     int validDays, String keyAlg, int keySize, String keyPW) {
    450588        if (ks.exists()) {
  • core/java/src/net/i2p/crypto/SU3File.java

    r651c1b6 r981b708  
    1616import java.security.PrivateKey;
    1717import java.security.PublicKey;
     18import java.security.cert.X509Certificate;
     19import java.security.cert.X509CRL;
    1820import java.util.ArrayList;
    1921import java.util.Arrays;
     
    3436import net.i2p.data.Signature;
    3537import net.i2p.data.SimpleDataStructure;
     38import net.i2p.util.SecureFileOutputStream;
    3639
    3740/**
     
    539542            String ftype = null;
    540543            String kfile = null;
     544            String crlfile = null;
    541545            String kspass = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD;
    542546            boolean error = false;
    543547            boolean shouldVerify = true;
    544             Getopt g = new Getopt("SU3File", args, "t:c:f:k:xp:");
     548            Getopt g = new Getopt("SU3File", args, "t:c:f:k:xp:r:");
    545549            int c;
    546550            while ((c = g.getopt()) != -1) {
     
    560564                case 'k':
    561565                    kfile = g.getOptarg();
     566                    break;
     567
     568                case 'r':
     569                    crlfile = g.getOptarg();
    562570                    break;
    563571
     
    599607                ok = verifySigCLI(a.get(0), kfile);
    600608            } else if ("keygen".equals(cmd)) {
    601                 ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2), kspass);
     609                Properties props = new Properties();
     610                props.setProperty("prng.bufferSize", "16384");
     611                new I2PAppContext(props);
     612                ok = genKeysCLI(stype, a.get(0), a.get(1), crlfile, a.get(2), kspass);
    602613            } else if ("extract".equals(cmd)) {
    603614                ok = extractCLI(a.get(0), a.get(1), shouldVerify, kfile);
     
    615626
    616627    private static final void showUsageCLI() {
    617         System.err.println("Usage: SU3File keygen       [-t type|code] [-p keystorepw] publicKeyFile keystore.ks you@mail.i2p\n" +
     628        System.err.println("Usage: SU3File keygen       [-t type|code] [-p keystorepw] [-r crlFile.crl] publicKeyFile.crt keystore.ks you@mail.i2p\n" +
    618629                           "       SU3File sign         [-t type|code] [-c type|code] [-f type|code] [-p keystorepw] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p\n" +
    619630                           "       SU3File bulksign     [-t type|code] [-c type|code] [-p keystorepw] directory keystore.ks version you@mail.i2p\n" +
     631                           "                            (signs all .zip and .xml files in the directory)\n" +
    620632                           "       SU3File showversion  signedFile.su3\n" +
    621633                           "       SU3File verifysig    [-k file.crt] signedFile.su3  ## -k use this pubkey cert for verification\n" +
     
    751763        for (File in : files) {
    752764            String inputFile = in.getPath();
    753             if (!inputFile.endsWith(".zip"))
     765            if (!inputFile.endsWith(".zip") && !inputFile.endsWith(".xml"))
    754766                continue;
    755767            String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3";
     
    898910
    899911    /**
     912     *  @param crlFile may be null; non-null to save
    900913     *  @return success
    901914     *  @since 0.9.9
    902915     */
    903916    private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile,
    904                                             String alias, String kspass) {
     917                                            String crlFile, String alias, String kspass) {
    905918        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
    906919        if (type == null) {
     
    908921            return false;
    909922        }
    910         return genKeysCLI(type, publicKeyFile, privateKeyFile, alias, kspass);
     923        return genKeysCLI(type, publicKeyFile, privateKeyFile, crlFile, alias, kspass);
    911924    }
    912925
    913926    /**
    914927     *  Writes Java-encoded keys (X.509 for public and PKCS#8 for private)
     928     *
     929     *  @param crlFile may be null; non-null to save
    915930     *  @return success
    916931     *  @since 0.9.9
    917932     */
    918933    private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile,
    919                                             String alias, String kspass) {
     934                                            String crlFile, String alias, String kspass) {
    920935        File pubFile = new File(publicKeyFile);
    921936        if (pubFile.exists()) {
     
    949964            return false;
    950965        }
    951         int keylen = type.getPubkeyLen() * 8;
    952         if (type.getBaseAlgorithm() == SigAlgo.EC) {
    953             keylen /= 2;
    954             if (keylen == 528)
    955                 keylen = 521;
    956         }
    957         boolean success = KeyStoreUtil.createKeys(ksFile, kspass, alias,
    958                                                   alias, "I2P", 3652, type.getBaseAlgorithm().getName(),
    959                                                   keylen, keypw);
    960         if (!success) {
     966        OutputStream out = null;
     967        try {
     968            Object[] rv =  KeyStoreUtil.createKeysAndCRL(ksFile, kspass, alias,
     969                                                         alias, "I2P", 3652, type, keypw);
     970            X509Certificate cert = (X509Certificate) rv[2];
     971            out = new SecureFileOutputStream(publicKeyFile);
     972            CertUtil.exportCert(cert, out);
     973            if (crlFile != null) {
     974                out.close();
     975                X509CRL crl = (X509CRL) rv[3];
     976                out = new SecureFileOutputStream(crlFile);
     977                CertUtil.exportCRL(crl, out);
     978            }
     979        } catch (GeneralSecurityException gse) {
    961980            System.err.println("Error creating keys for " + alias);
    962             return false;
    963         }
    964         File outfile = new File(publicKeyFile);
    965         success = KeyStoreUtil.exportCert(ksFile, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, alias, outfile);
    966         if (!success) {
    967             System.err.println("Error writing public key for " + alias + " to " + outfile);
    968             return false;
     981            gse.printStackTrace();
     982            return false;
     983        } catch (IOException ioe) {
     984            System.err.println("Error creating keys for " + alias);
     985            ioe.printStackTrace();
     986            return false;
     987        } finally {
     988            if (out != null) try { out.close(); } catch (IOException ioe) {}
    969989        }
    970990        return true;
  • router/java/src/net/i2p/router/crypto/FamilyKeyCrypto.java

    r651c1b6 r981b708  
    99import java.security.PrivateKey;
    1010import java.security.PublicKey;
     11import java.security.cert.X509Certificate;
     12import java.security.cert.X509CRL;
    1113import java.util.HashMap;
    1214import java.util.Map;
     
    5254    public static final String PROP_KEY_PASSWORD = "netdb.family.keyPassword";
    5355    public static final String CERT_SUFFIX = ".crt";
     56    public static final String CRL_SUFFIX = ".crl";
    5457    public static final String KEYSTORE_PREFIX = "family-";
    5558    public static final String KEYSTORE_SUFFIX = ".ks";
     
    6265    private static final String KS_DIR = "keystore";
    6366    private static final String CERT_DIR = "certificates/family";
     67    private static final String CRL_DIR = "crls";
    6468    public static final String OPT_NAME = "family";
    6569    public static final String OPT_SIG = "family.sig";
     
    271275            }
    272276        }
    273         createKeyStore(ks);
    274 
    275         // Now read it back out of the new keystore and save it in ascii form
    276         // where the clients can get to it.
    277         exportCert(ks);
     277
     278        try {
     279            createKeyStore(ks);
     280        } catch (IOException ioe) {
     281            throw new GeneralSecurityException("Failed to create NetDb family keystore", ioe);
     282        }
    278283    }
    279284
     
    287292     * @throws GeneralSecurityException on all errors
    288293     */
    289     private void createKeyStore(File ks) throws GeneralSecurityException {
     294    private void createKeyStore(File ks) throws GeneralSecurityException, IOException {
    290295        // make a random 48 character password (30 * 8 / 5)
    291296        String keyPassword = KeyStoreUtil.randomString();
     
    293298        String cname = _fname + CN_SUFFIX;
    294299
    295         boolean success = KeyStoreUtil.createKeys(ks, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, _fname, cname, "family",
     300        Object[] rv = KeyStoreUtil.createKeysAndCRL(ks, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, _fname, cname, "family",
    296301                                                  DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM,
    297302                                                  DEFAULT_KEY_SIZE, keyPassword);
    298         if (success) {
    299             success = ks.exists();
    300             if (success) {
     303
    301304                Map<String, String> changes = new HashMap<String, String>();
    302305                changes.put(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD);
     
    304307                changes.put(PROP_FAMILY_NAME, _fname);
    305308                _context.router().saveConfig(changes, null);
    306             }
    307         }
    308         if (success) {
     309
    309310            _log.logAlways(Log.INFO, "Created new private key for netdb family \"" + _fname +
    310311                           "\" in keystore: " + ks.getAbsolutePath() + "\n" +
     
    315316                           PROP_KEY_PASSWORD + '=' + keyPassword);
    316317
    317         } else {
    318             String s = "Failed to create NetDb family keystore.\n" +
    319                        "This is for the Sun/Oracle keytool, others may be incompatible.\n" +
    320                        "If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
    321                        " to " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath();
    322             _log.error(s);
    323             throw new GeneralSecurityException(s);
    324         }
    325     }
    326 
    327     /**
    328      * Pull the cert back OUT of the keystore and save it as ascii
     318        X509Certificate cert = (X509Certificate) rv[2];
     319        exportCert(cert);
     320        X509CRL crl = (X509CRL) rv[3];
     321        exportCRL(ks.getParentFile(), crl);
     322    }
     323
     324    /**
     325     * Save the public key certificate
    329326     * so the clients can get to it.
    330327     */
    331     private void exportCert(File ks) {
     328    private void exportCert(X509Certificate cert) {
    332329        File sdir = new SecureDirectory(_context.getConfigDir(), CERT_DIR);
    333330        if (sdir.exists() || sdir.mkdirs()) {
    334             String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD);
    335331            String name = _fname.replace("@", "_at_") + CERT_SUFFIX;
    336332            File out = new File(sdir, name);
    337             boolean success = KeyStoreUtil.exportCert(ks, ksPass, _fname, out);
     333            boolean success = CertUtil.saveCert(cert, out);
    338334            if (success) {
    339335                _log.logAlways(Log.INFO, "Created new public key certificate for netdb family \"" + _fname +
     
    343339                           "Give this certificate to an I2P developer for inclusion in the next I2P release.");
    344340            } else {
    345                 _log.error("Error getting SSL cert to save as ASCII");
     341                _log.error("Error saving family key certificate");
    346342            }
    347343        } else {
    348             _log.error("Error saving ASCII SSL keys");
     344            _log.error("Error saving family key certificate");
     345        }
     346    }
     347
     348    /**
     349     * Save the CRL just in case.
     350     * @param ksdir parent of directory to save in
     351     * @since 0.9.25
     352     */
     353    private void exportCRL(File ksdir, X509CRL crl) {
     354        File sdir = new SecureDirectory(ksdir, CRL_DIR);
     355        if (sdir.exists() || sdir.mkdirs()) {
     356            String name = KEYSTORE_PREFIX + _fname.replace("@", "_at_") + '-' + System.currentTimeMillis() + CRL_SUFFIX;
     357            File out = new File(sdir, name);
     358            boolean success = CertUtil.saveCRL(crl, out);
     359            if (success) {
     360                _log.logAlways(Log.INFO, "Created certificate revocation list (CRL) for netdb family \"" + _fname +
     361                           "\" in file: " + out.getAbsolutePath() + "\n" +
     362                           "Back up the keystore and CRL files and keep them secure.\n" +
     363                           "If your private key is ever compromised, give the CRL to an I2P developer for publication.");
     364            } else {
     365                _log.error("Error saving family key CRL");
     366            }
     367        } else {
     368            _log.error("Error saving family key CRL");
    349369        }
    350370    }
Note: See TracChangeset for help on using the changeset viewer.