/* * [PrivateKey.java] * * Summary: Information about a Private RSA key. * * Copyright: (c) 2004-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.1 2005-09-16 */ package com.mindprod.transporter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.security.SecureRandom; /** * Information about a Private RSA key. * * @author Roedy Green, Canadian Mind Products * @version 1.1 2005-09-16 * @since 2004-06-08 */ public final class PrivateKey extends PublicKey { /** * secret prime factor of n. */ public BigInteger d; /** * constructor * * @param privateKeyStream where to find serialised private key information. * * @throws IOException if cannot read the private key */ public PrivateKey( InputStream privateKeyStream ) throws IOException { loadKey( privateKeyStream ); } /** * Creates a brand new private key. Takes a long time. * * @param keySize Size of the key, 512 .. 4096. The larger the keySize the more secure, but the slower the program. * Must be a multiple of 8. Most commonly 1024 or 2048. */ public PrivateKey( int keySize ) { if ( keySize < Tools.SMALLEST_KEYSIZE || keySize > Tools.BIGGEST_KEYSIZE ) { throw new IllegalArgumentException( "keySize must be " + Tools .SMALLEST_KEYSIZE + ".." + Tools .BIGGEST_KEYSIZE ); } if ( keySize % 8 != 0 ) { throw new IllegalArgumentException( "keySize must be multiple of 8 bits" ); } this.keySize = keySize; boolean repeat; do { try { /* * can't use probablePrime, because in JDK 1.3 because it is * private */ BigInteger p = new BigInteger( keySize, 100, new SecureRandom() ); BigInteger q = new BigInteger( keySize, 100, new SecureRandom() ); /* calc the private key, a prime */ BigInteger product = p.subtract( BigInteger.ONE ) .multiply( q.subtract( BigInteger.ONE ) ); d = Tools.smallPrime.modInverse( product ); /* calc the public key, a product of two primes */ n = p.multiply( q ); repeat = false; } catch ( ArithmeticException e ) { // Not to worry, the odds are very high it will get this on // the first try, if not the second. It won't loop forever. repeat = true; } } while ( repeat ); } /** * Save the Privatekey as a serialised object which contains both private and public keys. Can use savePublicKey to * separately save the public portion of the key. * * @param os OutputStream to contain the key * * @throws IOException if cannot save the private key */ void savePrivateKey( OutputStream os ) throws IOException { // We avoid serialisation here even though the rest of the package // depends on it to make it easier for somone to cannibalise // a serialisation-free version. // O P E N DataOutputStream dos = new DataOutputStream( os ); // W R I T E dos.writeInt( keySize ); /* public key, preceded by length */ byte[] exportn = n.toByteArray(); dos.writeInt( exportn.length ); dos.write( exportn, 0, exportn.length ); /* private key, preceded by length */ byte[] exportd = d.toByteArray(); dos.writeInt( exportd.length ); dos.write( exportd, 0, exportd.length ); // C L O S E dos.close(); } /** * load a previously prepared private key. * * @param privateKeyStream where to look for the private key, might be on disk, in a resource, in a jar, on the web * etc. * * @throws IOException */ public void loadKey( InputStream privateKeyStream ) throws IOException { try { DataInputStream dis = new DataInputStream( privateKeyStream ); // R E A D // two keyes n and d each precedid by length int. keySize = dis.readInt(); // public key preceeded by length int nsize = dis.readInt(); byte[] importn = new byte[ nsize ]; final int actualnSize = dis.read( importn ); if ( actualnSize != nsize ) { throw new IOException( "Private d key corrupt" ); } n = new BigInteger( importn ); // private key preceeded by length int dsize = dis.readInt(); byte[] importd = new byte[ dsize ]; final int actualdSize = dis.read( importd ); if ( actualdSize != dsize ) { throw new IOException( "Private n key corrupt" ); } d = new BigInteger( importd ); // C L O S E dis.close(); } catch ( IOException e ) { throw new IOException( "Corrupt private key " + e.getMessage() ); } } }