Changeset 5d1d8b6


Ignore:
Timestamp:
Jan 31, 2016 10:09:39 PM (5 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
bf51d5d9
Parents:
129fb973
Message:

SelfSigned?: Add support for CRL generation

Location:
core/java/src/net/i2p/crypto
Files:
2 edited

Legend:

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

    r129fb973 r5d1d8b6  
    1616import java.security.cert.CertificateFactory;
    1717import java.security.cert.CertificateEncodingException;
     18import java.security.cert.CRLException;
    1819import java.security.cert.X509Certificate;
     20import java.security.cert.X509CRL;
    1921import java.security.spec.PKCS8EncodedKeySpec;
    2022import java.security.spec.KeySpec;
     
    328330        }
    329331    }
     332
     333    /**
     334     *  Write a CRL to a file in base64 format.
     335     *
     336     *  @return success
     337     *  @since 0.9.25
     338     */
     339    public static boolean saveCRL(X509CRL crl, File file) {
     340        OutputStream os = null;
     341        try {
     342           os = new SecureFileOutputStream(file);
     343           exportCRL(crl, os);
     344           return true;
     345        } catch (CRLException ce) {
     346            error("Error writing X509 CRL " + file.getAbsolutePath(), ce);
     347           return false;
     348        } catch (IOException ioe) {
     349            error("Error writing X509 CRL " + file.getAbsolutePath(), ioe);
     350           return false;
     351        } finally {
     352            try { if (os != null) os.close(); } catch (IOException foo) {}
     353        }
     354    }
     355
     356    /**
     357     *  Writes a CRL in base64 format.
     358     *  Does NOT close the stream. Throws on all errors.
     359     *
     360     *  @throws CRLException if the crl does not support encoding
     361     *  @since 0.9.25
     362     */
     363    private static void exportCRL(X509CRL crl, OutputStream out)
     364                                                throws IOException, CRLException {
     365        byte[] buf = crl.getEncoded();
     366        if (buf == null)
     367            throw new CRLException("encoding unsupported for this CRL");
     368        PrintWriter wr = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
     369        wr.println("-----BEGIN X509 CRL-----");
     370        String b64 = Base64.encode(buf, true);     // true = use standard alphabet
     371        for (int i = 0; i < b64.length(); i += LINE_LENGTH) {
     372            wr.println(b64.substring(i, Math.min(i + LINE_LENGTH, b64.length())));
     373        }
     374        wr.println("-----END X509 CRL-----");
     375        wr.flush();
     376        if (wr.checkError())
     377            throw new IOException("Failed write to " + out);
     378    }
     379
     380/****
     381    public static final void main(String[] args) {
     382        if (args.length < 2) {
     383            System.out.println("Usage: [loadcert | loadcrl | loadprivatekey] file");
     384            System.exit(1);
     385        }
     386        try {
     387            File f = new File(args[1]);
     388            if (args[0].equals("loadcert")) {
     389                loadCert(f);
     390            } else if (args[0].equals("loadcrl")) {
     391            } else {
     392            }
     393
     394        } catch (Exception e) {
     395            e.printStackTrace();
     396            System.exit(1);
     397        }
     398    }
     399****/
    330400}
  • core/java/src/net/i2p/crypto/SelfSignedGenerator.java

    r129fb973 r5d1d8b6  
    44import java.io.ByteArrayOutputStream;
    55import java.io.File;
     6import java.io.FileOutputStream;
    67import java.io.IOException;
    7 import java.io.ObjectInputStream;
    8 import java.io.ObjectOutputStream;
    98import java.math.BigInteger;
    109import java.security.GeneralSecurityException;
     
    1413import java.security.cert.CertificateFactory;
    1514import java.security.cert.X509Certificate;
     15import java.security.cert.X509CRL;
    1616import java.security.spec.X509EncodedKeySpec;
    1717import java.text.SimpleDateFormat;
     
    3636import net.i2p.util.HexDump;
    3737import net.i2p.util.RandomSource;
     38import net.i2p.util.SecureFileOutputStream;
    3839import net.i2p.util.SystemVersion;
    3940
     
    6162    // Subject Key Identifier
    6263    private static final String OID_SKI = "2.5.29.14";
     64    // CRL number
     65    private static final String OID_CRLNUM = "2.5.29.20";
    6366
    6467    private static final Map<String, String> OIDS;
     
    7881     *  rv[1] is a Java PrivateKey
    7982     *  rv[2] is a Java X509Certificate
     83     *  rv[3] is a Java X509CRL
    8084     */
    8185    public static Object[] generate(String cname, String ou, String o, String l, String st, String c,
     
    97101            case RSA_SHA512_4096:
    98102            case EdDSA_SHA512_Ed25519:
     103            case EdDSA_SHA512_Ed25519ph:
    99104                oid = type.getOID();
    100105                break;
     
    153158            throw new GeneralSecurityException("cert error", iae);
    154159        }
     160        X509CRL crl = generateCRL(cert, validDays, 1, sigoid, jpriv);
    155161
    156162        // some simple tests
     
    159165        if (!cpub.equals(jpub))
    160166            throw new GeneralSecurityException("pubkey mismatch");
    161 
    162         Object[] rv = { jpub, jpriv, cert };
     167        // todo crl tests
     168
     169        Object[] rv = { jpub, jpriv, cert, crl };
     170        return rv;
     171    }
     172
     173    /**
     174     *  Generate a CRL for the given cert, signed with the given private key
     175     */
     176    private static X509CRL generateCRL(X509Certificate cert, int validDays, int crlNum,
     177                                       byte[] sigoid, PrivateKey jpriv) throws GeneralSecurityException {
     178
     179        SigningPrivateKey priv = SigUtil.fromJavaKey(jpriv);
     180
     181        byte[] tbs = genTBSCRL(cert, validDays, crlNum, sigoid);
     182        int tbslen = tbs.length;
     183
     184        Signature sig = DSAEngine.getInstance().sign(tbs, priv);
     185        if (sig == null)
     186            throw new GeneralSecurityException("sig failed");
     187        byte[] sigbytes= SigUtil.toJavaSig(sig);
     188
     189        int seqlen = tbslen + sigoid.length + spaceFor(sigbytes.length + 1);
     190        int totlen = spaceFor(seqlen);
     191        byte[] cb = new byte[totlen];
     192        int idx = 0;
     193
     194        // construct the whole encoded cert
     195        cb[idx++] = 0x30;
     196        idx = intToASN1(cb, idx, seqlen);
     197
     198        // TBS cert
     199        System.arraycopy(tbs, 0, cb, idx, tbs.length);
     200        idx += tbs.length;
     201
     202        // sig algo
     203        System.arraycopy(sigoid, 0, cb, idx, sigoid.length);
     204        idx += sigoid.length;
     205
     206        // sig (bit string)
     207        cb[idx++] = 0x03;
     208        idx = intToASN1(cb, idx, sigbytes.length + 1);
     209        cb[idx++] = 0;
     210        System.arraycopy(sigbytes, 0, cb, idx, sigbytes.length);
     211
     212     /****
     213        if (DEBUG) {
     214            System.out.println("CRL Sig OID");
     215            System.out.println(HexDump.dump(sigoid));
     216            System.out.println("CRL Signature");
     217            System.out.println(HexDump.dump(sigbytes));
     218            System.out.println("Whole CRL");
     219            System.out.println(HexDump.dump(cb));
     220        }
     221      ****/
     222
     223        ByteArrayInputStream bais = new ByteArrayInputStream(cb);
     224
     225        X509CRL rv;
     226        try {
     227            CertificateFactory cf = CertificateFactory.getInstance("X.509");
     228            // wow, unlike for x509Certificates, there's no validation here at all
     229            // ASN.1 errors don't cause any exceptions
     230            rv = (X509CRL)cf.generateCRL(bais);
     231        } catch (IllegalArgumentException iae) {
     232            throw new GeneralSecurityException("cert error", iae);
     233        }
     234
    163235        return rv;
    164236    }
     
    232304
    233305    /**
     306     *
     307     *  @param crlNum 0-255 because lazy
     308     *  @return ASN.1 encoded object
     309     */
     310    private static byte[] genTBSCRL(X509Certificate cert, int validDays,
     311                                    int crlNum, byte[] sigalg) throws GeneralSecurityException {
     312        // a0 ???, int = 2
     313        byte[] version = { 2, 1, 1 };
     314        byte[] issuer = cert.getIssuerX500Principal().getEncoded();
     315
     316        byte[] serial = cert.getSerialNumber().toByteArray();
     317        if (serial.length > 255)
     318            throw new IllegalArgumentException();
     319        long now = System.currentTimeMillis();
     320        long then = now + (validDays * 24L * 60 * 60 * 1000);
     321        // used for CRL time and revocation time
     322        byte[] nowbytes = getDate(now);
     323        // used for next CRL time
     324        byte[] thenbytes = getDate(then);
     325
     326        byte[] extbytes = getCRLExtensions(crlNum);
     327
     328        int revlen = 2 + serial.length + nowbytes.length;
     329        int revseqlen = spaceFor(revlen);
     330        int revsseqlen = spaceFor(revseqlen);
     331
     332
     333        int len = version.length + sigalg.length + issuer.length + nowbytes.length +
     334                  thenbytes.length + revsseqlen + extbytes.length;
     335
     336        int totlen = spaceFor(len);
     337        byte[] rv = new byte[totlen];
     338        int idx = 0;
     339        rv[idx++] = 0x30;
     340        idx = intToASN1(rv, idx, len);
     341        System.arraycopy(version, 0, rv, idx, version.length);
     342        idx += version.length;
     343        System.arraycopy(sigalg, 0, rv, idx, sigalg.length);
     344        idx += sigalg.length;
     345        System.arraycopy(issuer, 0, rv, idx, issuer.length);
     346        idx += issuer.length;
     347        System.arraycopy(nowbytes, 0, rv, idx, nowbytes.length);
     348        idx += nowbytes.length;
     349        System.arraycopy(thenbytes, 0, rv, idx, thenbytes.length);
     350        idx += thenbytes.length;
     351        // the certs
     352        rv[idx++] = 0x30;
     353        idx = intToASN1(rv, idx, revseqlen);
     354        // the cert
     355        rv[idx++] = 0x30;
     356        idx = intToASN1(rv, idx, revlen);
     357        rv[idx++] = 0x02;
     358        rv[idx++] = (byte) serial.length;
     359        System.arraycopy(serial, 0, rv, idx, serial.length);
     360        idx += serial.length;
     361        System.arraycopy(nowbytes, 0, rv, idx, nowbytes.length);
     362        idx += nowbytes.length;
     363        // extensions
     364        System.arraycopy(extbytes, 0, rv, idx, extbytes.length);
     365
     366        if (DEBUG) {
     367            System.out.println("version");
     368            System.out.println(HexDump.dump(version));
     369            System.out.println("sigalg");
     370            System.out.println(HexDump.dump(sigalg));
     371            System.out.println("issuer");
     372            System.out.println(HexDump.dump(issuer));
     373            System.out.println("now");
     374            System.out.println(HexDump.dump(nowbytes));
     375            System.out.println("then");
     376            System.out.println(HexDump.dump(thenbytes));
     377            System.out.println("serial");
     378            System.out.println(HexDump.dump(serial));
     379            System.out.println("extensions");
     380            System.out.println(HexDump.dump(extbytes));
     381            System.out.println("TBS CRL");
     382            System.out.println(HexDump.dump(rv));
     383        }
     384        return rv;
     385    }
     386
     387    /**
    234388     *  @param val the length of the value, 65535 max
    235389     *  @return the length of the TLV
     
    247401
    248402    /**
     403     *  Sequence of two UTCDates
    249404     *  @return 32 bytes ASN.1 encoded object
    250405     */
    251406    private static byte[] getValidity(int validDays) {
    252         ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
    253         baos.write(0x30);
    254         // len to be filled in later
    255         baos.write(0);
     407        byte[] rv = new byte[32];
     408        rv[0] = 0x30;
     409        rv[1] = 30;
    256410        long now = System.currentTimeMillis();
    257411        long then = now + (validDays * 24L * 60 * 60 * 1000);
     412        byte[] nowbytes = getDate(now);
     413        byte[] thenbytes = getDate(then);
     414        System.arraycopy(nowbytes, 0, rv, 2, 15);
     415        System.arraycopy(thenbytes, 0, rv, 17, 15);
     416        return rv;
     417    }
     418
     419    /**
     420     *  A single UTCDate
     421     *  @return 15 bytes ASN.1 encoded object
     422     */
     423    private static byte[] getDate(long now) {
    258424        // UTCDate format (HH 0-23)
    259425        SimpleDateFormat fmt = new SimpleDateFormat("yyMMddHHmmss");
    260426        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    261427        byte[] nowbytes = DataHelper.getASCII(fmt.format(new Date(now)));
    262         byte[] thenbytes = DataHelper.getASCII(fmt.format(new Date(then)));
    263         baos.write(0x17);
    264         baos.write(nowbytes.length + 1);
    265         baos.write(nowbytes, 0, nowbytes.length);
    266         baos.write('Z');
    267         baos.write(0x17);
    268         baos.write(thenbytes.length + 1);
    269         baos.write(thenbytes, 0, thenbytes.length);
    270         baos.write('Z');
    271         byte[] rv = baos.toByteArray();
    272         rv[1] = (byte) (rv.length - 2);
     428        if (nowbytes.length != 12)
     429            throw new IllegalArgumentException();
     430        byte[] rv = new byte[15];
     431        rv[0] = 0x17;
     432        rv[1] = 13;
     433        System.arraycopy(nowbytes, 0, rv, 2, 12);
     434        rv[14] = (byte) 'Z';
    273435        return rv;
    274436    }
     
    308470        System.arraycopy(oid, 0, rv, idx, oid.length);
    309471        idx += oid.length;
    310         // don't know why we wrap the int in another int
     472        // don't know why we wrap the octet string in an octet string
    311473        rv[idx++] = (byte) 0x04;
    312474        idx = intToASN1(rv, idx, wraplen);
     
    314476        idx = intToASN1(rv, idx, sha.length);
    315477        System.arraycopy(sha, 0, rv, idx, sha.length);
     478        return rv;
     479    }
     480
     481    /**
     482     *
     483     *  @param crlNum 0-255 because lazy
     484     *  @return 16 bytes ASN.1 encoded object
     485     */
     486    private static byte[] getCRLExtensions(int crlNum) {
     487        if (crlNum < 0 || crlNum > 255)
     488            throw new IllegalArgumentException();
     489        byte[] oid = getEncodedOID(OID_CRLNUM);
     490        int extlen = oid.length + 5;
     491        int extslen = spaceFor(extlen);
     492        int seqlen = spaceFor(extslen);
     493        int totlen = spaceFor(seqlen);
     494        byte[] rv = new byte[totlen];
     495        int idx = 0;
     496        rv[idx++] = (byte) 0xa0;
     497        idx = intToASN1(rv, idx, seqlen);
     498        rv[idx++] = (byte) 0x30;
     499        idx = intToASN1(rv, idx, extslen);
     500        rv[idx++] = (byte) 0x30;
     501        idx = intToASN1(rv, idx, extlen);
     502        System.arraycopy(oid, 0, rv, idx, oid.length);
     503        idx += oid.length;
     504        // don't know why we wrap the int in an octet string
     505        rv[idx++] = (byte) 0x04;
     506        rv[idx++] = (byte) 3;
     507        rv[idx++] = (byte) 0x02;
     508        rv[idx++] = (byte) 1;
     509        rv[idx++] = (byte) crlNum;
    316510        return rv;
    317511    }
     
    379573            test("test6", SigType.RSA_SHA512_4096);
    380574            test("test7", SigType.EdDSA_SHA512_Ed25519);
     575            test("test8", SigType.EdDSA_SHA512_Ed25519ph);
    381576        } catch (Exception e) {
    382577            e.printStackTrace();
     
    389584            PrivateKey jpriv = (PrivateKey) rv[1];
    390585            X509Certificate cert = (X509Certificate) rv[2];
     586            X509CRL crl = (X509CRL) rv[3];
    391587            File ks = new File(name + ".ks");
    392588            List<X509Certificate> certs = new ArrayList<X509Certificate>(1);
     
    397593            CertUtil.saveCert(cert, cf);
    398594            System.out.println("Certificate saved to " + cf);
     595            File pf = new File(name + ".priv");
     596            FileOutputStream pfs = new SecureFileOutputStream(pf);
     597            KeyStoreUtil.exportPrivateKey(ks, "changeit", "foo", "foobar", pfs);
     598            pfs.close();
     599            System.out.println("Private key saved to " + pf);
     600            File cr = new File(name + ".crl");
     601            CertUtil.saveCRL(crl, cr);
     602            System.out.println("CRL saved to " + cr);
    399603    }
    400604****/
Note: See TracChangeset for help on using the changeset viewer.