Java: How check if JVM is 64bits?

A veces es util saber si estas corriendo una JVM de 32 o 64 bits.

public class Check64bitsJVM {
	public static void main(final String[] args) throws Throwable {
		System.out.println("JVM is 64bits?: " + JVMis64bits());
	}
	public static boolean JVMis64bits() {
		final String propOsArch = System.getProperty("os.arch");
		final String propSunDataModel = System.getProperty("sun.arch.data.model");
		System.out.println("os.arch: " + propOsArch);
		System.out.println("sun.arch.data.model: " + propSunDataModel);
		// 
		if (propSunDataModel != null) {
			return propSunDataModel.equals("64");
		}
		if (propOsArch != null) {
			return propOsArch.contains("64");
		}
		return false;
	}
}

Leer más de este artículo

Lubuntu: Disable automount (LXDE)

Si te has vuelto mico buscando como desactivar el automount de un dispositivo USB en Lubuntu/LXDE:

Este comando es la solución:

pcmanfm

lubuntu pcmanfm disable automount

La pista (pq por mas que busqué en la configuración no encontré nada) fue el anuncio de la pagina de Lubuntu:

== Features ==
* Based on the lightweight LXDE desktop environment.
* Pcmanfm 0.9.10, a fast and lightweight files manager using gio/gvfs.
* Openbox, the fast and extensible, default windows-manager of LXDE.
* Lightdm, using the simple GTK greeter
* Chromium, the open-source version of Google Chrome.
* … and, of course, based on Ubuntu 12.04 LTS.

See the complete list of applications on https://wiki.ubuntu.com/Lubuntu/Applications

Otros metodos para otros sistemas:
Disable automount in Nautilus

Java: Repositorio Maven con Amazon S3

Si quieres montarte un repositorio Maven en Amazon S3 sin calentarte mucho la cabeza con un Nexus-OSS, aqui va el resumen (en los enlaces -ingles- los detalles completos):

Primero debes crear un par de buckets en S3, REPONAME-release y REPONAME-snapshot.
Nota: Donde REPONAME podria ser algo como por ejemplo: com-acme-maven-repo
Luego debes crear un usuario en el servicio IAM, eso te dara un Access Key ID que usaras como “username” y un Secret Access Key que usaras como “passphrase” en el settings.xml

Una vez creado el usuario debes adjuntarle una policy en la seccion de Permissions como esta:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::REPONAME-snapshot",
        "arn:aws:s3:::REPONAME-snapshot/*", 
        "arn:aws:s3:::REPONAME-release", 
        "arn:aws:s3:::REPONAME-release/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListAllMyBuckets",
      "Resource": "arn:aws:s3:::REPONAME-*"
    }
  ]
}

Debes configurar las credenciales que se usaran para acceder a los buckets del S3 (~/.m2/settings.xml)

<settings>
    ...cut...
    <servers>
        ...cut...
        <server>
            <id>maven-s3-release-repo</id>
            <username>0123456789ABCDEFGHIJ</username>
            <passphrase>0123456789abcdefghijklmnopqrstuvwxyzABCD</passphrase>
        </server>
        <server>
            <id>maven-s3-snapshot-repo</id>
            <username>0123456789ABCDEFGHIJ</username>
            <passphrase>0123456789abcdefghijklmnopqrstuvwxyzABCD</passphrase>
        </server>
        ...cut...
    </servers>
    ...cut...
</settings>

Luego en el pom.xml de tu proyecto le indicas que repositorio debe usar para la publicacion:

<project ...cut...>
    <build>
        <extensions>
            <extension>
                <groupId>org.springframework.build</groupId>
                <artifactId>aws-maven</artifactId>
                <version>4.4.0.RELEASE</version>
            </extension>
        </extensions>
    </build>
    <repositories>
        <repository>
            <id>maven-s3-release-repo</id>
            <name>S3 Release Repository</name>
            <url>s3://REPONAME-release/release</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>maven-s3-snapshot-repo</id>
            <name>S3 Snapshot Repository</name>
            <url>s3://REPONAME-snapshot/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>maven-s3-release-repo</id>
            <name>S3 Release Repository</name>
            <url>s3://REPONAME-release/release</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>maven-s3-snapshot-repo</id>
            <name>S3 Snapshot Repository</name>
            <url>s3://REPONAME-snapshot/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <distributionManagement>
        <repository>
            <id>maven-s3-release-repo</id>
            <name>S3 Release Repository</name>
            <url>s3://REPONAME-release/release</url>
        </repository>
        <snapshotRepository>
            <id>maven-s3-snapshot-repo</id>
            <name>S3 Snapshot Repository</name>
            <url>s3://REPONAME-snapshot/snapshot</url>
        </snapshotRepository>
    </distributionManagement>
</project>

Y para terminar… maven deploy

Fuentes y detalles:
Hosting Maven repository on Amazon-S3
Giving access to a single S3 Bucket using Amazon IAM
Maven Wagon for Amazon S3

Java: Logs de Aplicaciones – La senda del Guerrero

Lógica básica a la hora de poner trazas:

  • No des por hecho que tienes acceso al código/subversión:
    Si tu traza (INFO/WARN/ERROR) necesita de leer el código, es que no es una buena traza.
  • No des por hecho que tienes trazas de nivel DEBUG:
    Si tienes que tener las trazas en modo debug es que necesitas una traza bien definida en un nivel inferior, ya sea ERROR, WARN o INFO.
  • No des por hecho que solo accede 1 usuario a tu aplicación:
    Las trazas se mezclan, haz que sean identificables y se puedan correlacionar las diferentes líneas de log y así saber que líneas pertenecen a que petición o usuario.
  • Ni mucha, ni poca; pon información útil:
    No hace falta que pongas el quijote, tampoco pongas un dato que no se sepa que es.
  • No pongas acentos; si las pones en ingles, mejor:
    Los acentos no siempre son ASCII/ISO-8859-1, los ficheros de logs a veces se ven mal o los “greps” no funcionan bien.

Ejemplo de traza “mala” (que no aporta nada útil, ni tiene contexto de referencia) (y se repite 100 veces sin ninguna diferencia):

2012-02-07 14:04:30,471 [catalina-exec-35] INFO  com.acme.coyote.servlets.UploadServlet - Buscando caracteres no compatibles en: ESTA TARDE VA A SALIR EL SOL

Ejemplo de traza “buena” (que aporta información útil):

2012-02-16 14:42:10,504 ERROR com.acme.XXXX [10.242.1.8:ACME:PEDRO] [Login] Error en password company=ACME user=PEDRO pwd=<NULL>

Usando MDC/NDC (log4j) para mejorar la trazabilidad

Ejemplo de uso/implementación (si usas threads “extras” o workers hay q trabajar un poco más este ejemplo para heredar/pasar la información de contexto, ya que los datos de MDC/NDC son “ThreadLocal”):

import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.NDC;

// log4j.appender.XXXX.layout.ConversionPattern=%d{ISO8601} %p %c [%X{IP}:%X{SESSION}:%x] %m%n

public class MyClass extends HttpServlet {
    
    private final static Logger l = Logger.getLogger(MyClass.class);

    // [...blablabla...]

    public void doGet(final HttpServletRequest req, final HttpServletResponse resp) 
                throws IOException, ServletException {
        try {
            MDC.put("IP", req.getRemoteAddr()); // MDC IP=10.242.1.8
            MDC.put("SESSION", req.getRequestedSessionId()); // MDC SESSION=7A7B9C9D721EAAFFAA1BB7E43BDFD2F1
            //
            NDC.push("doGet"); // NDC (add entry point)
            //
            String p_user = req.getParameter("user"));

            // Check Params
            if (p_user == null) {
                l.error("Invalid parameters USER=<NULL>");
                // 2012-02-16 16:42:10,504 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet] Invalid parameters USER=<NULL>
                // [...blablabla...]
                return;
            }
            
            // Remapping Parameters
            p_user = p_user.trim().toUpperCase();
            
            NDC.push("USER=" + p_user); // NDC (add user info)

            try {
                NDC.push("Test");
                l.info("blablabla");
                // 2012-02-16 16:42:10,801 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE Test] blablabla
                // [...blablabla...]
            }
            finally {
                NDC.pop();
            }

            // [...blablabla...]

            l.info("Request OK");
            // 2012-02-16 16:42:11,102 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE] Request OK
        }
        catch (Exception e) {
            l.error("Request ERROR (Exception=" + e.toString() + ")");
            // 2012-02-16 16:42:11,102 ERROR com.acme.MyClass [10.242.1.8:7A7B9C9D721EAAFFAA1BB7E43BDFD2F1:doGet USER=PEPE] Request ERROR (Exception=java.lang.NullPointerException)
        }
        finally {
            NDC.clear();
            MDC.clear();
        }
    }

    // [...blablabla...]
}

Referencias:
NDC vs MDC – Which one should I use?
Log4j MDC (Mapped Diagnostic Context) : Example code
Build Flexible Logs With log4j
Effective logging practices ease enterprise development

Java: Atomic, volatile, synchronized?

Otro sencillo benchmark para tener un orden de magnitud de que es más rápido:

B (boolean), VB (volatile boolean), AB (AtomicBoolean), Bsync (boolean synchronized)

B	100000000	900ms	100000000req/s	111111req/ms	best (reference)
VB	100000000	2497ms	50000000req/s	40048req/ms	2nd better
AB	100000000	2513ms	50000000req/s	39793req/ms	3rd better
Bsync	100000000	5715ms	20000000req/s	17497req/ms	worse

El codigo de prueba:

import java.util.concurrent.atomic.AtomicBoolean;

public class TestBoolean {
	private static boolean b = true;
	private static volatile boolean vb = true;
	private static final AtomicBoolean ab = new AtomicBoolean(true);
	//
	private static final void setAB(final boolean pb) {
		ab.set(pb);
	}
	private static final void setVB(final boolean pb) {
		vb = pb;
	}
	private static final void setB(final boolean pb) {
		b = pb;
	}
	private static final synchronized void setBsync(final boolean pb) {
		b = pb;
	}
	//
	public static void main(String[] args) {
		final int TOTAL = (int) 1e8;
		long ts, diff;
		// Do anything
		System.out.println("B\t" + b);
		System.out.println("VB\t" + vb);
		System.out.println("AB\t" + ab.get());
		//
		// boolean
		ts = System.currentTimeMillis();
		for (int i = 0; i < TOTAL; i++) {
			setB(((i % 2) == 0) ? true : false);
		}
		diff = (System.currentTimeMillis() - ts);
		System.out.println("B\t" + TOTAL + "\t" + diff + "ms\t"
				+ (TOTAL / Math.max(diff / 1000, 1)) + "req/s\t"
				+ (TOTAL / Math.max(diff, 1)) + "req/ms");
		//
		// synchronized boolean 
		ts = System.currentTimeMillis();
		for (int i = 0; i < TOTAL; i++) {
			setBsync(((i % 2) == 0) ? true : false);
		}
		diff = (System.currentTimeMillis() - ts);
		System.out.println("Bsync\t" + TOTAL + "\t" + diff + "ms\t"
				+ (TOTAL / Math.max(diff / 1000, 1)) + "req/s\t"
				+ (TOTAL / Math.max(diff, 1)) + "req/ms");
		//
		// volatile boolean
		ts = System.currentTimeMillis();
		for (int i = 0; i < TOTAL; i++) {
			setVB(((i % 2) == 0) ? true : false);
		}
		diff = (System.currentTimeMillis() - ts);
		System.out.println("VB\t" + TOTAL + "\t" + diff + "ms\t"
				+ (TOTAL / Math.max(diff / 1000, 1)) + "req/s\t"
				+ (TOTAL / Math.max(diff, 1)) + "req/ms");
		//
		// AtomicBoolean
		ts = System.currentTimeMillis();
		for (int i = 0; i < TOTAL; i++) {
			setAB(((i % 2) == 0) ? true : false);
		}
		diff = (System.currentTimeMillis() - ts);
		System.out.println("AB\t" + TOTAL + "\t" + diff + "ms\t"
				+ (TOTAL / Math.max(diff / 1000, 1)) + "req/s\t"
				+ (TOTAL / Math.max(diff, 1)) + "req/ms");
		// Do anything
		System.out.println("B\t" + b);
		System.out.println("VB\t" + vb);
		System.out.println("AB\t" + ab.get());
	}
}

Referencias:
Patrones de uso para volatile
Managing volatility
Synchronized Methods
Atomic Variables

Java: Right Bit Shift

Que diferencia hay entre los dos operadores de rotación de bits “>>” (signed) y “>>>” (unsigned)?

Una imagen vale más que mil palabras:

v	10000001001001001000010000010010
v>>1	11000000100100100100001000001001
v>>>1	01000000100100100100001000001001
v>>2	11100000010010010010000100000100
v>>>2	00100000010010010010000100000100
v>>3	11110000001001001001000010000010
v>>>3	00010000001001001001000010000010
-----
v	01000001001001001000010000010010
v>>1	00100000100100100100001000001001
v>>>1	00100000100100100100001000001001
v>>2	00010000010010010010000100000100
v>>>2	00010000010010010010000100000100
v>>3	00001000001001001001000010000010
v>>>3	00001000001001001001000010000010
-----
v	17
v>>1	8
v>>>1	8
v	-17
v>>1	-9
v>>>1	2147483639

Como puede verse, “>>>” rellenará por la izquierda (bit más alto) con “0″, mientras que “>>” rellenará por la izquierda con “0″ si el bit más alto es “0″ y con “1″ si el bit más alto es “1″.

Y el código de prueba:

public class TestBits {
	public static void main(String[] args) {
		int v;
		v = 0x81248412;
		System.out.println("v\t" + pad(Integer.toBinaryString(v)));
		System.out.println("v>>1\t" + pad(Integer.toBinaryString(v>>1)));
		System.out.println("v>>>1\t" + pad(Integer.toBinaryString(v>>>1)));
		System.out.println("v>>2\t" + pad(Integer.toBinaryString(v>>2)));
		System.out.println("v>>>2\t" + pad(Integer.toBinaryString(v>>>2)));
		System.out.println("v>>3\t" + pad(Integer.toBinaryString(v>>3)));
		System.out.println("v>>>3\t" + pad(Integer.toBinaryString(v>>>3)));
		System.out.println("-----");
		v = 0x41248412;
		System.out.println("v\t" + pad(Integer.toBinaryString(v)));
		System.out.println("v>>1\t" + pad(Integer.toBinaryString(v>>1)));
		System.out.println("v>>>1\t" + pad(Integer.toBinaryString(v>>>1)));
		System.out.println("v>>2\t" + pad(Integer.toBinaryString(v>>2)));
		System.out.println("v>>>2\t" + pad(Integer.toBinaryString(v>>>2)));
		System.out.println("v>>3\t" + pad(Integer.toBinaryString(v>>3)));
		System.out.println("v>>>3\t" + pad(Integer.toBinaryString(v>>>3)));
		System.out.println("-----");
		v = 17;
		System.out.println("v\t" + v);
		System.out.println("v>>1\t" + (v>>1));
		System.out.println("v>>>1\t" + (v>>>1));
		v = -17;
		System.out.println("v\t" + v);
		System.out.println("v>>1\t" + (v>>1));
		System.out.println("v>>>1\t" + (v>>>1));
	}
	private static String pad(String v) {
		return "000000000000000000000000000000000".substring(0, 32-v.length()) + v;
	}
}

Referencia:
Bitwise and Bit Shift Operators)

Java: Why not use mapped files? Because they are unpredictable!

Usar ficheros mmapped en Java es en el mejor de los casos… un dolor de cabeza (a menos que uses una JVM de 64bits -en cuyo caso el problema queda camuflado-). Nada que ver con las llamadas en C que funcionan muy bien y son perfectamente predecibles. Ante la duda… no lo uses en Java, a la larga tu codigo se romperá menos.

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.Random;

/**
 * Why not use java mapped files? Because they are unpredictable!
 * <br/> In Windows:
 * <br/> 	java.lang.OutOfMemoryError: Map failed
 * <br/> In Linux: 
 * <br/> 	can work, if you are lucky ;-) 
 */
public class TestMappedFile {
	private static final Random r = new Random();
	private static final long filesize = 128 * 1024 * 1024;

	public static void main(String[] args) throws Exception {
		try {
			for (int i = 1; i < 128; i++) {
				System.out.println("Creating: " + i + " with size " + filesize);
				create(i);
			}
		} catch (Throwable t) {
			// WTF? Yeah... mmap sucks
			t.printStackTrace(System.out);
		}
	}
	private static void create(final int i) throws IOException {
		final File file = new File("/tmp/mmap." + i + ".tmp");
		final RandomAccessFile raf = new RandomAccessFile(file, "rw");
		raf.setLength(filesize);
		MappedByteBuffer buf = null;
		try {
			final int offset = (r.nextInt() & 0xFFFF);
			final byte b = (byte) (r.nextInt() & 0xFF);
			final FileChannel channel = raf.getChannel();
			// Section of file to map
			buf = channel.map(MapMode.READ_WRITE, 0L, channel.size()-1);
			// do something with buf
			buf.clear();
			write(buf, 0, b);
			write(buf, offset, b);
			write(buf, buf.limit()-1, b);
			buf.force();
			channel.force(false);
		} finally {
			//unmapMmapedWithInternalSunAPI(buf); // Non-portable clean method
			// In theory: Mapping remains until file is closed
			raf.close();
			buf = null;
			if (!file.delete()) {
				System.out.println("WTF? Can not delete file " + file.getAbsolutePath());
			}
		}
	}
	private static void write(final ByteBuffer buf, final int offset, final byte b) {
		System.out.println("Writing " + b + " in offset " + offset + "/" + buf.limit());
		buf.put(offset, b);
	}
	// Can crash with: java.lang.Error: Cleaner terminated abnormally
	//private static void unmapMmapedWithInternalSunAPI(final ByteBuffer buf) {
	//	if ((buf != null) && (buf instanceof sun.nio.ch.DirectBuffer)) {
	//		final sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buf).cleaner();
	//		cleaner.clean();
	//	}
	//}
}

Referencias:
Sun Bug
How unmap? You can’t

Java: Simple Bouncer v1.5.0 final

Reverse Tunneling Release de la version 1.5.0 de SimpleBouncer, un Proxy TCP comparable a rinetd con algunas funcionalidades extra inspiradas en stunnel o ssh, como tunneling SSL, tuneles reversos y failover/loadbalancing de conexiones.

SimpleBouncer in GitHub

Java: El poltergeist (arrays, negativos y excepciones)

Despues de varias horas volviendome literalmente… loco, he descubierto que algo que en teoria es imposible, para Java no lo es…

Aqui el error (aparece de modo aleatorio):

java.lang.ArrayIndexOutOfBoundsException: -2
        at RandomTextGenerator$1.run(RandomTextGenerator.java:38)
        ... cut ...

El error no me parecería extraño (no se puede usar un numero negativo como indice para un array), si no fuera porque tengo el codigo delante y ya estaba controlada esa posibilidad: si el numero es menor de cero, le cambia el signo, por tanto era literalmente imposible esa excepcion y aún así… salta!

private static final char[] chars = "0123456789ab...".toCharArray(); // Linea 18
// ... cut ...
final int n = rand.nextInt(); // Linea 37
os.write(chars[(n < 0 ? -n : n) % chars.length]); // Linea 38

Por mas que lo miro, no lo veo… ¿Como es posible que diga -2 si nunca debería devolver un numero negativo? Alucinante. Esto tendrían q sacarlo en un Guinness.

EDICION: Poltergeist descubierto.
Para un cierto valor [Integer.MIN_VALUE=(-2^31)=(-2147483648)] el resultado del cambio de signo “-N” no varía… -2147483648.
Yo lo catalogaría de bug; pero al parecer en una recóndita nota del API de Java [Math.abs(int n)] ya hablan algo sobre el tema.
PD2: En C tambien sucede (parece que he sido afortunado durante años).

El codigo fuente completo en GitHub

Java: undocumented sun.security.x509

Generando certificados autofirmados X.509 usando las (tristemente) indocumentadas sun.security.x509; la alternativa es usar Bouncy Castle (que cambian las APIs de una version a otra)… :-(

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Vector;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.xml.bind.DatatypeConverter;

import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import sun.security.x509.BasicConstraintsExtension;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.ExtendedKeyUsageExtension;
import sun.security.x509.KeyIdentifier;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

public class TestX509 {

	/**
	 * Create a self-signed X.509 Certificate
	 * @param dn the X.509 Distinguished Name, eg "CN=Test"
	 * @param pair the KeyPair
	 * @param days how many days from now the Certificate is valid for
	 */ 
	static X509Certificate generateCertificate(String dn, KeyPair pair, int days) throws Exception {
		PrivateKey privkey = pair.getPrivate();
		X509CertInfo info = new X509CertInfo();
		Date from = new Date();
		Date to = new Date(from.getTime() + days * 86400000L);
		CertificateValidity interval = new CertificateValidity(from, to);
		int sn = (int)((System.currentTimeMillis()/1000) & 0xFFFFFFFF);
		X500Name owner = new X500Name(dn);

		AlgorithmId algo = new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid);
		info.set(X509CertInfo.VALIDITY, interval);
		info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
		info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
		info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
		info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
		info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
		info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

		// Extensions
		CertificateExtensions ext = new CertificateExtensions();
		ext.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(Boolean.TRUE, true, 0)); // Critical|isCA|pathLen
		ext.set(SubjectKeyIdentifierExtension.NAME, new SubjectKeyIdentifierExtension(new KeyIdentifier(pair.getPublic()).getIdentifier()));
		ext.set(AuthorityKeyIdentifierExtension.NAME, new AuthorityKeyIdentifierExtension(new KeyIdentifier(pair.getPublic()), null, null));
		// Extended Key Usage Extension
		Vector<ObjectIdentifier> ekue = new Vector<ObjectIdentifier>();
		ekue.add(new ObjectIdentifier(new int[] { 1, 3, 6, 1, 5, 5, 7, 3, 1 })); // Server
		ekue.add(new ObjectIdentifier(new int[] { 1, 3, 6, 1, 5, 5, 7, 3, 2 })); // Client
		ext.set(ExtendedKeyUsageExtension.NAME, new ExtendedKeyUsageExtension(Boolean.FALSE, ekue));
		info.set(X509CertInfo.EXTENSIONS, ext);

		// Sign the X.509
		X509CertImpl cert = new X509CertImpl(info);
		cert.sign(privkey, algo.getName());
		return cert;
	}

	static void writeCertificate(OutputStream out, X509Certificate crt) throws Exception {
		final int BLOCK_SIZE = 64;
		byte[] buf =  DatatypeConverter.printBase64Binary(crt.getEncoded()).getBytes();
		out.write("-----BEGIN CERTIFICATE-----\r\n".getBytes());
		for (int i = 0; i < buf.length; i += BLOCK_SIZE) {
			out.write(buf, i, Math.min(BLOCK_SIZE, buf.length - i));
			out.write('\r');
			out.write('\n');
		}
		out.write("-----END CERTIFICATE-----\r\n".getBytes());
		out.flush();
		out.close();
	}

	static void writeKey(OutputStream out,  PrivateKey pk) throws Exception {
		final int BLOCK_SIZE = 64;
		byte[] buf =  DatatypeConverter.printBase64Binary(pk.getEncoded()).getBytes();
		out.write("-----BEGIN RSA PRIVATE KEY-----\r\n".getBytes());
		for (int i = 0; i < buf.length; i += BLOCK_SIZE) {
			out.write(buf, i, Math.min(BLOCK_SIZE, buf.length - i));
			out.write('\r');
			out.write('\n');
		}
		out.write("-----END RSA PRIVATE KEY-----\r\n".getBytes());
		out.flush();
		out.close();
	}

	public static void main(String[] args) throws Exception {
		KeyPairGenerator kgAsym = KeyPairGenerator.getInstance("RSA");
		kgAsym.initialize(1024); // TODO: RSA { 1024, 1536, 2048 }
		KeyPair kp = kgAsym.genKeyPair();
		X509Certificate crt = generateCertificate("CN=Test1", kp, 365);
		System.out.println(crt);
		File keyFile = new File(System.getProperty("java.io.tmpdir"), "test.key");
		File crtFile = new File(System.getProperty("java.io.tmpdir"), "test.crt");
		writeKey(new FileOutputStream(keyFile), kp.getPrivate());
		writeCertificate(new FileOutputStream(crtFile), crt);
	}

}

Fuente Original: BFO*

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

%d bloggers like this: