forked from I2P_Developers/i2p.i2p
Set alt names for console cert Use utils to validate console IP addresses
This commit is contained in:
@ -42,6 +42,8 @@ import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.I2PSSLSocketFactory;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
@ -516,17 +518,15 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
try {
|
||||
// Test before we add the connector, because Jetty 6 won't start if any of the
|
||||
// connectors are bad
|
||||
InetAddress test = InetAddress.getByName(host);
|
||||
if ((!hasIPV6) && (!(test instanceof Inet4Address)))
|
||||
if ((!hasIPV6) && InetAddressUtils.isIPv6Address(host))
|
||||
throw new IOException("IPv6 addresses unsupported");
|
||||
if ((!hasIPV4) && (test instanceof Inet4Address))
|
||||
if ((!hasIPV4) && InetAddressUtils.isIPv4Address(host))
|
||||
throw new IOException("IPv4 addresses unsupported");
|
||||
ServerSocket testSock = null;
|
||||
try {
|
||||
// On Windows, this was passing and Jetty was still failing,
|
||||
// possibly due to %scope_id ???
|
||||
// https://issues.apache.org/jira/browse/ZOOKEEPER-667
|
||||
//testSock = new ServerSocket(0, 0, test);
|
||||
// so do exactly what Jetty does in SelectChannelConnector.open()
|
||||
testSock = new ServerSocket();
|
||||
InetSocketAddress isa = new InetSocketAddress(host, 0);
|
||||
@ -574,7 +574,23 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
}
|
||||
if (sslPort > 0) {
|
||||
File keyStore = new File(_context.getConfigDir(), "keystore/console.ks");
|
||||
if (verifyKeyStore(keyStore)) {
|
||||
// Put the list of hosts together early, so we can put it in the selfsigned cert.
|
||||
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
|
||||
Set<String> altNames = new HashSet<String>(4);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String s = tok.nextToken().trim();
|
||||
if (!s.equals("0.0.0.0") && !s.equals("::") &&
|
||||
!s.equals("0:0:0:0:0:0:0:0"))
|
||||
altNames.add(s);
|
||||
}
|
||||
String allowed = _context.getProperty(PROP_ALLOWED_HOSTS);
|
||||
if (allowed != null) {
|
||||
tok = new StringTokenizer(allowed, " ,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
altNames.add(tok.nextToken().trim());
|
||||
}
|
||||
}
|
||||
if (verifyKeyStore(keyStore, altNames)) {
|
||||
// the keystore path and password
|
||||
SslContextFactory sslFactory = new SslContextFactory(keyStore.getAbsolutePath());
|
||||
sslFactory.setKeyStorePassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD));
|
||||
@ -585,22 +601,20 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
sslFactory.addExcludeCipherSuites(I2PSSLSocketFactory.EXCLUDE_CIPHERS.toArray(
|
||||
new String[I2PSSLSocketFactory.EXCLUDE_CIPHERS.size()]));
|
||||
List<String> hosts = new ArrayList<String>(2);
|
||||
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
|
||||
tok = new StringTokenizer(_sslListenHost, " ,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String host = tok.nextToken().trim();
|
||||
// doing it this way means we don't have to escape an IPv6 host with []
|
||||
try {
|
||||
// Test before we add the connector, because Jetty 6 won't start if any of the
|
||||
// connectors are bad
|
||||
InetAddress test = InetAddress.getByName(host);
|
||||
if ((!hasIPV6) && (!(test instanceof Inet4Address)))
|
||||
if ((!hasIPV6) && InetAddressUtils.isIPv6Address(host))
|
||||
throw new IOException("IPv6 addresses unsupported");
|
||||
if ((!hasIPV4) && (test instanceof Inet4Address))
|
||||
if ((!hasIPV4) && InetAddressUtils.isIPv4Address(host))
|
||||
throw new IOException("IPv4 addresses unsupported");
|
||||
ServerSocket testSock = null;
|
||||
try {
|
||||
// see comments above
|
||||
//testSock = new ServerSocket(0, 0, test);
|
||||
testSock = new ServerSocket();
|
||||
InetSocketAddress isa = new InetSocketAddress(host, 0);
|
||||
testSock.bind(isa);
|
||||
@ -839,30 +853,28 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
* @return success if it exists and we have a password, or it was created successfully.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private boolean verifyKeyStore(File ks) {
|
||||
private boolean verifyKeyStore(File ks, Set<String> altNames) {
|
||||
if (ks.exists()) {
|
||||
boolean rv = _context.getProperty(PROP_KEY_PASSWORD) != null;
|
||||
if (!rv)
|
||||
System.err.println("Console SSL error, must set " + PROP_KEY_PASSWORD + " in " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath());
|
||||
return rv;
|
||||
}
|
||||
return createKeyStore(ks);
|
||||
return createKeyStore(ks, altNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call out to keytool to create a new keystore with a keypair in it.
|
||||
* Trying to do this programatically is a nightmare, requiring either BouncyCastle
|
||||
* libs or using proprietary Sun libs, and it's a huge mess.
|
||||
* Create a new keystore with a keypair in it.
|
||||
*
|
||||
* @return success
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private boolean createKeyStore(File ks) {
|
||||
private boolean createKeyStore(File ks, Set<String> altNames) {
|
||||
// make a random 48 character password (30 * 8 / 5)
|
||||
String keyPassword = KeyStoreUtil.randomString();
|
||||
String cname = "localhost";
|
||||
boolean success = KeyStoreUtil.createKeys(ks, "console", cname, "Console", keyPassword);
|
||||
boolean success = KeyStoreUtil.createKeys(ks, "console", cname, altNames, "Console", keyPassword);
|
||||
if (success) {
|
||||
success = ks.exists();
|
||||
if (success) {
|
||||
@ -883,7 +895,8 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
}
|
||||
if (success) {
|
||||
System.err.println("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
|
||||
"The certificate was generated randomly, and is not associated with your " +
|
||||
"The certificate was generated randomly.\n" +
|
||||
"Unless you have changed the default settings, the certificate is not associated with your " +
|
||||
"IP address, host name, router identity, or destination keys.");
|
||||
} else {
|
||||
System.err.println("Failed to create console SSL keystore.\n" +
|
||||
|
@ -467,7 +467,30 @@ public final class KeyStoreUtil {
|
||||
*/
|
||||
public static boolean createKeys(File ks, String alias, String cname, String ou,
|
||||
String keyPW) {
|
||||
return createKeys(ks, DEFAULT_KEYSTORE_PASSWORD, alias, cname, ou,
|
||||
return createKeys(ks, DEFAULT_KEYSTORE_PASSWORD, alias, cname, null, ou,
|
||||
DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM, DEFAULT_KEY_SIZE, keyPW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a keypair and store it in the keystore at ks, creating it if necessary.
|
||||
* Use default keystore password, valid days, algorithm, and key size.
|
||||
*
|
||||
* Warning, may take a long time.
|
||||
*
|
||||
* @param ks path to the keystore
|
||||
* @param alias the name of the key
|
||||
* @param cname e.g. localhost. Must be a hostname or email address. IP addresses will not be correctly encoded.
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou e.g. console
|
||||
* @param keyPW the key password, must be at least 6 characters
|
||||
*
|
||||
* @return success
|
||||
* @since 0.9.34 added altNames param
|
||||
*/
|
||||
public static boolean createKeys(File ks, String alias, String cname, Set<String> altNames, String ou,
|
||||
String keyPW) {
|
||||
return createKeys(ks, DEFAULT_KEYSTORE_PASSWORD, alias, cname, altNames, ou,
|
||||
DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM, DEFAULT_KEY_SIZE, keyPW);
|
||||
}
|
||||
|
||||
@ -494,12 +517,42 @@ public final class KeyStoreUtil {
|
||||
*/
|
||||
public static boolean createKeys(File ks, String ksPW, String alias, String cname, String ou,
|
||||
int validDays, String keyAlg, int keySize, String keyPW) {
|
||||
return createKeys(ks, ksPW, alias, cname, null, ou, validDays, keyAlg, keySize, keyPW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a keypair and store it in the keystore at ks, creating it if necessary.
|
||||
*
|
||||
* For new code, the createKeysAndCRL() with the SigType argument is recommended over this one,
|
||||
* as it throws exceptions, and returns the certificate and CRL.
|
||||
*
|
||||
* Warning, may take a long time.
|
||||
*
|
||||
* @param ks path to the keystore
|
||||
* @param ksPW the keystore password
|
||||
* @param alias the name of the key
|
||||
* @param cname e.g. localhost. Must be a hostname or email address. IP addresses will not be correctly encoded.
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou e.g. console
|
||||
* @param validDays e.g. 3652 (10 years)
|
||||
* @param keyAlg e.g. DSA , RSA, EC
|
||||
* @param keySize e.g. 1024
|
||||
* @param keyPW the key password, must be at least 6 characters
|
||||
*
|
||||
* @return success
|
||||
* @since 0.9.34 added altNames param
|
||||
*/
|
||||
public static boolean createKeys(File ks, String ksPW, String alias, String cname, Set<String> altNames, String ou,
|
||||
int validDays, String keyAlg, int keySize, String keyPW) {
|
||||
boolean useKeytool = I2PAppContext.getGlobalContext().getBooleanProperty("crypto.useExternalKeytool");
|
||||
if (useKeytool) {
|
||||
if (altNames != null)
|
||||
throw new IllegalArgumentException("can't do SAN in keytool");
|
||||
return createKeysCLI(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
|
||||
} else {
|
||||
try {
|
||||
createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
|
||||
createKeysAndCRL(ks, ksPW, alias, cname, altNames, ou, validDays, keyAlg, keySize, keyPW);
|
||||
return true;
|
||||
} catch (GeneralSecurityException gse) {
|
||||
error("Create keys error", gse);
|
||||
@ -546,6 +599,46 @@ public final class KeyStoreUtil {
|
||||
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
|
||||
int validDays, String keyAlg, int keySize, String keyPW)
|
||||
throws GeneralSecurityException, IOException {
|
||||
return createKeysAndCRL(ks, ksPW, alias, cname, null, ou, validDays, keyAlg, keySize, keyPW);
|
||||
}
|
||||
|
||||
/**
|
||||
* New way - Native Java, does not call out to keytool.
|
||||
* Create a keypair and store it in the keystore at ks, creating it if necessary.
|
||||
*
|
||||
* This returns the public key, private key, certificate, and CRL in an array.
|
||||
* All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
|
||||
* The private key and selfsigned cert are stored in the keystore.
|
||||
* The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
|
||||
* The public key certificate may be stored separately with
|
||||
* CertUtil.saveCert() if desired.
|
||||
* The CRL is not stored by this method, store it with
|
||||
* CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
|
||||
*
|
||||
* Throws on all errors.
|
||||
* Warning, may take a long time.
|
||||
*
|
||||
* @param ks path to the keystore
|
||||
* @param ksPW the keystore password
|
||||
* @param alias the name of the key
|
||||
* @param cname e.g. localhost. Must be a hostname or email address. IP addresses will not be correctly encoded.
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou e.g. console
|
||||
* @param validDays e.g. 3652 (10 years)
|
||||
* @param keyAlg e.g. DSA , RSA, EC
|
||||
* @param keySize e.g. 1024
|
||||
* @param keyPW the key password, must be at least 6 characters
|
||||
* @return all you need:
|
||||
* rv[0] is a Java PublicKey
|
||||
* rv[1] is a Java PrivateKey
|
||||
* rv[2] is a Java X509Certificate
|
||||
* rv[3] is a Java X509CRL
|
||||
* @since 0.9.34 added altNames param
|
||||
*/
|
||||
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, Set<String> altNames, String ou,
|
||||
int validDays, String keyAlg, int keySize, String keyPW)
|
||||
throws GeneralSecurityException, IOException {
|
||||
String algoName = getSigAlg(keySize, keyAlg);
|
||||
SigType type = null;
|
||||
for (SigType t : EnumSet.allOf(SigType.class)) {
|
||||
@ -556,7 +649,7 @@ public final class KeyStoreUtil {
|
||||
}
|
||||
if (type == null)
|
||||
throw new GeneralSecurityException("Unsupported algorithm/size: " + keyAlg + '/' + keySize);
|
||||
return createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, type, keyPW);
|
||||
return createKeysAndCRL(ks, ksPW, alias, cname, altNames, ou, validDays, type, keyPW);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -592,13 +685,51 @@ public final class KeyStoreUtil {
|
||||
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
|
||||
int validDays, SigType type, String keyPW)
|
||||
throws GeneralSecurityException, IOException {
|
||||
return createKeysAndCRL(ks, ksPW, alias, cname, null, ou, validDays, type, keyPW);
|
||||
}
|
||||
|
||||
/**
|
||||
* New way - Native Java, does not call out to keytool.
|
||||
* Create a keypair and store it in the keystore at ks, creating it if necessary.
|
||||
*
|
||||
* This returns the public key, private key, certificate, and CRL in an array.
|
||||
* All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
|
||||
* The private key and selfsigned cert are stored in the keystore.
|
||||
* The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
|
||||
* The public key certificate may be stored separately with
|
||||
* CertUtil.saveCert() if desired.
|
||||
* The CRL is not stored by this method, store it with
|
||||
* CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
|
||||
*
|
||||
* Throws on all errors.
|
||||
* Warning, may take a long time.
|
||||
*
|
||||
* @param ks path to the keystore
|
||||
* @param ksPW the keystore password
|
||||
* @param alias the name of the key
|
||||
* @param cname e.g. localhost. Must be a hostname or email address. IP addresses will not be correctly encoded.
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou e.g. console
|
||||
* @param validDays e.g. 3652 (10 years)
|
||||
* @param keyPW the key password, must be at least 6 characters
|
||||
* @return all you need:
|
||||
* rv[0] is a Java PublicKey
|
||||
* rv[1] is a Java PrivateKey
|
||||
* rv[2] is a Java X509Certificate
|
||||
* rv[3] is a Java X509CRL
|
||||
* @since 0.9.34 added altNames param
|
||||
*/
|
||||
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, Set<String> altNames, String ou,
|
||||
int validDays, SigType type, String keyPW)
|
||||
throws GeneralSecurityException, IOException {
|
||||
File dir = ks.getParentFile();
|
||||
if (dir != null && !dir.exists()) {
|
||||
File sdir = new SecureDirectory(dir.getAbsolutePath());
|
||||
if (!sdir.mkdirs())
|
||||
throw new IOException("Can't create directory " + dir);
|
||||
}
|
||||
Object[] rv = SelfSignedGenerator.generate(cname, ou, "I2P", "I2P Anonymous Network", null, null, validDays, type);
|
||||
Object[] rv = SelfSignedGenerator.generate(cname, altNames, ou, "I2P", "I2P Anonymous Network", null, null, validDays, type);
|
||||
//PublicKey jpub = (PublicKey) rv[0];
|
||||
PrivateKey jpriv = (PrivateKey) rv[1];
|
||||
X509Certificate cert = (X509Certificate) rv[2];
|
||||
|
@ -18,8 +18,10 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
@ -27,12 +29,15 @@ import javax.crypto.spec.DHParameterSpec;
|
||||
import javax.crypto.spec.DHPublicKeySpec;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import static net.i2p.crypto.SigUtil.intToASN1;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
@ -42,10 +47,12 @@ import net.i2p.util.SystemVersion;
|
||||
* Generate keys and a selfsigned certificate, suitable for
|
||||
* storing in a Keystore with KeyStoreUtil.storePrivateKey().
|
||||
* All done programatically, no keytool, no BC libs, no sun classes.
|
||||
* Ref: RFC 2459
|
||||
* Ref: RFC 2459, RFC 5280
|
||||
*
|
||||
* This is coded to create a cert that is similar to what comes out of keytool,
|
||||
* even if I don't understand all of it.
|
||||
* This is coded to create a cert that is similar to what comes out of keytool.
|
||||
*
|
||||
* NOTE: Recommended use is via KeyStoreUtil.createKeys() and related methods.
|
||||
* This API may not be stable.
|
||||
*
|
||||
* @since 0.9.25
|
||||
*/
|
||||
@ -108,6 +115,29 @@ public final class SelfSignedGenerator {
|
||||
*/
|
||||
public static Object[] generate(String cname, String ou, String o, String l, String st, String c,
|
||||
int validDays, SigType type) throws GeneralSecurityException {
|
||||
return generate(cname, null, ou, o, l, st, c, validDays, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cname the common name, non-null. Must be a hostname or email address. IP addresses will not be correctly encoded.
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou The OU (organizational unit) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param o The O (organization)in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param l The L (city or locality) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param st The ST (state or province) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param c The C (country) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
*
|
||||
* @return length 4 array:
|
||||
* rv[0] is a Java PublicKey
|
||||
* rv[1] is a Java PrivateKey
|
||||
* rv[2] is a Java X509Certificate
|
||||
* rv[3] is a Java X509CRL
|
||||
*
|
||||
* @since 0.9.34 added altNames param
|
||||
*/
|
||||
public static Object[] generate(String cname, Set<String> altNames, String ou, String o, String l, String st, String c,
|
||||
int validDays, SigType type) throws GeneralSecurityException {
|
||||
SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||
SigningPublicKey pub = (SigningPublicKey) keys[0];
|
||||
SigningPrivateKey priv = (SigningPrivateKey) keys[1];
|
||||
@ -132,7 +162,7 @@ public final class SelfSignedGenerator {
|
||||
}
|
||||
byte[] sigoid = getEncodedOIDSeq(oid);
|
||||
|
||||
byte[] tbs = genTBS(cname, ou, o, l, st, c, validDays, sigoid, jpub);
|
||||
byte[] tbs = genTBS(cname, altNames, ou, o, l, st, c, validDays, sigoid, jpub);
|
||||
int tbslen = tbs.length;
|
||||
|
||||
Signature sig = DSAEngine.getInstance().sign(tbs, priv);
|
||||
@ -261,13 +291,15 @@ public final class SelfSignedGenerator {
|
||||
|
||||
/**
|
||||
* @param cname the common name, non-null
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @param ou The OU (organizational unit) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param o The O (organization)in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param l The L (city or locality) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param st The ST (state or province) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
* @param c The C (country) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
|
||||
*/
|
||||
private static byte[] genTBS(String cname, String ou, String o, String l, String st, String c,
|
||||
private static byte[] genTBS(String cname, Set<String> altNames, String ou, String o, String l, String st, String c,
|
||||
int validDays, byte[] sigoid, PublicKey jpub) throws GeneralSecurityException {
|
||||
// a0 ???, int = 2
|
||||
byte[] version = { (byte) 0xa0, 3, 2, 1, 2 };
|
||||
@ -298,7 +330,7 @@ public final class SelfSignedGenerator {
|
||||
byte[] subject = issuer;
|
||||
|
||||
byte[] pubbytes = jpub.getEncoded();
|
||||
byte[] extbytes = getExtensions(pubbytes, cname);
|
||||
byte[] extbytes = getExtensions(pubbytes, cname, altNames);
|
||||
|
||||
int len = version.length + serial.length + sigoid.length + issuer.length +
|
||||
validity.length + subject.length + pubbytes.length + extbytes.length;
|
||||
@ -494,9 +526,11 @@ public final class SelfSignedGenerator {
|
||||
* Ref: RFC 5280
|
||||
*
|
||||
* @param pubbytes bit string
|
||||
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
|
||||
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
|
||||
* @return ASN.1 encoded object
|
||||
*/
|
||||
private static byte[] getExtensions(byte[] pubbytes, String cname) {
|
||||
private static byte[] getExtensions(byte[] pubbytes, String cname, Set<String> altNames) {
|
||||
// RFC 2549 sec. 4.2.1.2
|
||||
// subject public key identifier is the sha1 hash of the bit string of the public key
|
||||
// without the tag, length, and igore fields
|
||||
@ -530,21 +564,27 @@ public final class SelfSignedGenerator {
|
||||
int wrap3len = spaceFor(TRUE.length);
|
||||
int ext3len = oid3.length + TRUE.length + spaceFor(wrap3len);
|
||||
|
||||
// TODO if IP address, encode as 4 or 16 bytes
|
||||
byte[] cnameBytes = DataHelper.getASCII(cname);
|
||||
int wrap41len = spaceFor(cnameBytes.length);
|
||||
// only used for CA
|
||||
byte[] ipv4;
|
||||
byte[] ipv6;
|
||||
int wrap41len = 0;
|
||||
if (altNames == null)
|
||||
altNames = new HashSet<String>(4);
|
||||
else
|
||||
altNames.remove("0:0:0:0:0:0:0:1"); // We don't want dup of "::1"
|
||||
altNames.add(cname);
|
||||
final boolean isCA = !cname.contains("@");
|
||||
if (isCA) {
|
||||
ipv4 = new byte[] { 127, 0, 0, 1 };
|
||||
ipv6 = new byte[16];
|
||||
ipv6[15] = 1;
|
||||
wrap41len += spaceFor(ipv4.length) + spaceFor(ipv6.length);
|
||||
} else {
|
||||
ipv4 = null;
|
||||
ipv6 = null;
|
||||
altNames.add("localhost");
|
||||
altNames.add("127.0.0.1");
|
||||
altNames.add("::1");
|
||||
}
|
||||
for (String n : altNames) {
|
||||
int len;
|
||||
if (InetAddressUtils.isIPv4Address(n))
|
||||
len = 4;
|
||||
else if (InetAddressUtils.isIPv6Address(n))
|
||||
len = 16;
|
||||
else
|
||||
len = n.length();
|
||||
wrap41len += spaceFor(len);
|
||||
}
|
||||
int wrap4len = spaceFor(wrap41len);
|
||||
int ext4len = oid4.length + spaceFor(wrap4len);
|
||||
@ -643,26 +683,26 @@ public final class SelfSignedGenerator {
|
||||
idx = intToASN1(rv, idx, ext4len);
|
||||
System.arraycopy(oid4, 0, rv, idx, oid4.length);
|
||||
idx += oid4.length;
|
||||
// octet string wraps a sequence containing a choice 2 (DNSName) IA5String
|
||||
// followed by two byteArrays (IP addresses)
|
||||
// octet string wraps a sequence containing the names and IP addresses
|
||||
rv[idx++] = (byte) 0x04;
|
||||
idx = intToASN1(rv, idx, wrap4len);
|
||||
rv[idx++] = (byte) 0x30;
|
||||
idx = intToASN1(rv, idx, wrap41len);
|
||||
// TODO if IP address, encode as 0x87
|
||||
for (String n : altNames) {
|
||||
byte[] b;
|
||||
if (InetAddressUtils.isIPv4Address(n) ||
|
||||
InetAddressUtils.isIPv6Address(n)) {
|
||||
b = Addresses.getIP(n);
|
||||
if (b == null) // shouldn't happen
|
||||
throw new IllegalArgumentException("fail " + n);
|
||||
rv[idx++] = (byte) 0x87; // choice, octet string for IP address
|
||||
} else {
|
||||
b = DataHelper.getASCII(n);
|
||||
rv[idx++] = (byte) (isCA ? 0x82 : 0x81); // choice, dNSName or rfc822Name, IA5String implied
|
||||
idx = intToASN1(rv, idx, cnameBytes.length);
|
||||
System.arraycopy(cnameBytes, 0, rv, idx, cnameBytes.length);
|
||||
idx += cnameBytes.length;
|
||||
if (isCA) {
|
||||
rv[idx++] = (byte) 0x87; // choice, octet string for IP address
|
||||
idx = intToASN1(rv, idx, ipv4.length);
|
||||
System.arraycopy(ipv4, 0, rv, idx, ipv4.length);
|
||||
idx += ipv4.length;
|
||||
rv[idx++] = (byte) 0x87; // choice, octet string for IP address
|
||||
idx = intToASN1(rv, idx, ipv6.length);
|
||||
System.arraycopy(ipv6, 0, rv, idx, ipv6.length);
|
||||
idx += ipv6.length;
|
||||
}
|
||||
idx = intToASN1(rv, idx, b.length);
|
||||
System.arraycopy(b, 0, rv, idx, b.length);
|
||||
idx += b.length;
|
||||
}
|
||||
|
||||
// Policy
|
||||
|
Reference in New Issue
Block a user