Construya aplicaciones seguras en red con certificados, Parte 2

Para crear aplicaciones seguras, debe aprender las herramientas del oficio. Para ayudarlo a familiarizarse con estos conceptos, le presenté la criptografía de clave pública en la Parte 1 y le expliqué cómo evita los problemas de intercambio de claves que acompañan a la criptografía de clave secreta. También exploré la relación entre la confianza y la escalabilidad de la criptografía de clave pública, y expliqué cómo los certificados y una infraestructura de clave pública (PKI) permiten la confianza en una escala más amplia que la que la criptografía de clave pública puede lograr por sí sola. Finalmente, describí los certificados y las cadenas de certificados, y expliqué cómo se relacionan con las CA (autoridades de certificación).

Hay muchos tipos diferentes de certificados disponibles, incluidos SDSI (infraestructura de seguridad distribuida simple), PGP (privacidad bastante buena) y X.509. Este mes, para ampliar aún más su vocabulario de seguridad, describiré el formato de certificado que lidera el paquete y es un componente clave de los estándares PKI emergentes: el certificado X.509.

Puede leer la serie completa sobre certificados:

  • Parte 1: Los certificados agregan valor a la criptografía de clave pública
  • Parte 2: aprender a usar los certificados X.509
  • Parte 3: usar las clases Java CRL y X509CRL
  • Parte 4: autenticar clientes y servidores y verificar cadenas de certificados

El formato X.509 en detalle

La Unión Internacional de Telecomunicaciones (UIT) desarrolló y publicó el formato de certificado X.509, que fue seleccionado por el grupo de trabajo de Infraestructura de clave pública X.509 (PKIX) del Grupo de trabajo de ingeniería de Internet (IETF). Si los acrónimos indican fuerza, X.509 claramente tiene poderosos aliados.

Usando una notación llamada ASN.1 (Notación de sintaxis abstracta uno), el estándar X.509 define el formato de un certificado. ASN.1 es un lenguaje estandarizado que describe tipos de datos abstractos de una manera independiente de la plataforma.

El documento "Infraestructura de clave pública X.509 de Internet - Certificado y perfil de CRL" (consulte Recursos para obtener un enlace) publicado por el grupo de trabajo PKIX describe un formato de certificado X.509 en términos de notación ASN.1. Es una lectura fascinante si estás interesado en ese tipo de cosas.

Un tipo de datos, como un certificado, definido en ASN.1 no es útil hasta que pueda definir sin ambigüedades cómo representar una instancia de un tipo de datos como una serie de bits. Para dar esa funcionalidad al tipo de datos, ASN.1 utiliza las Reglas de codificación distinguida (DER), que definen cómo codificar de forma única cualquier objeto ASN.1.

Con una copia de la definición ASN.1 de un certificado X.509 y un conocimiento del DER, puede escribir una aplicación Java que lea y escriba certificados X.509 e interopere con aplicaciones similares escritas en otros lenguajes de programación. Afortunadamente, probablemente nunca tendrá que tomarse tantas molestias porque Java 2 Platform, Standard Edition (J2SE) viene con soporte integrado para certificados X.509.

X.509 por (casi) nada

Todas las clases e interfaces relacionadas con el certificado residen en el paquete java.security.cert. Al igual que los otros miembros de la familia de API de seguridad de Sun, el paquete de certificados se diseñó en torno al paradigma de fábrica, en el que una o más clases de Java definen una interfaz genérica para la funcionalidad prevista de un paquete. Las clases son abstractas, por lo que las aplicaciones no pueden instanciarlas directamente. En cambio, la instancia de una clase de fábrica crea y devuelve instancias de los subtipos particulares de las clases abstractas. El paradigma de fábrica evita la tipificación fuerte de Java, pero a cambio, permite que el código se ejecute sin recompilación en una gama más amplia de entornos.

Las clases abstractas java.security.cert.Certificatey java.security.cert.CRLdefinen la interfaz. Representan certificados y listas de revocación de certificados (CRL), respectivamente. La CertificateFactoryclase es su fábrica.

El java.security.certpaquete contiene implementaciones concretas de las clases abstractas Certificatey CRL: las clases X509Certificatey X509CRL. Estas dos clases implementan la funcionalidad básica de certificado y CRL y luego la amplían con la funcionalidad específica de X.509. Cuando una CertificateFactoryinstancia devuelve una instancia de cualquiera de las clases, un programa puede usarla tal como está o convertirla explícitamente en el formulario X.509.

En el java.security.certpaquete, la interfaz X509Extensiondefine una interfaz para las extensiones de un certificado X.509. Las extensiones son componentes opcionales que proporcionan un mecanismo para que los creadores de certificados asocien información adicional con un certificado. Por ejemplo, un certificado puede usar la KeyUsageextensión para indicar que se puede usar para la firma de código.

El java.security.certpaquete también incluye una clase de interfaz de proveedor de servicios (SPI). Un proveedor de servicios criptográficos que desea admitir un tipo de certificado extiende el SPI. Java 2 viene con un SPI para certificados X.509.

Echemos un vistazo más detallado a las clases e interfaces del java.security.certpaquete. En aras de la brevedad, discutiré solo los métodos más útiles. Para una cobertura más completa, le animo a leer la documentación de Sun. (Ver Recursos).

java.security.cert.CertificateFactory

La historia comienza con java.security.cert.CertificateFactory. La CertificateFactoryclase tiene métodos estáticos que crean una CertificateFactoryinstancia para un tipo específico de certificado y métodos que crean certificados y CRL a partir de datos proporcionados en un flujo de entrada. Describiré brevemente los métodos más importantes y luego explicaré cómo utilizar estos métodos al generar certificados X.509 y CRL. Más adelante en el artículo, presentaré el código que demuestra los métodos en acción.

  • public static CertificateFactory getInstance(String stringType)y public static CertificateFactory getInstance(String stringType, String stringProvider)crear una instancia y devolver una instancia de una fábrica de certificados para el tipo de certificado especificado por el stringTypeparámetro. Por ejemplo, si el valor de stringTypees la cadena "X.509", ambos métodos devolverán una instancia de la CertificateFactoryclase adecuada para crear instancias de las clases X509Certificatey X509CRL. El segundo método acepta el nombre de un proveedor de servicios criptográficos específico como argumento y usa ese proveedor en lugar del predeterminado.
  • public final Certificate generateCertificate(InputStream inputstream)crea una instancia y devuelve un certificado utilizando los datos leídos de la InputStreaminstancia proporcionada . Si la secuencia contiene más de un certificado y la secuencia admite las operaciones mark()y reset(), el método leerá un certificado y dejará la secuencia antes del siguiente.
  • public final Collection generateCertificates(InputStream inputstream)crea una instancia y devuelve una colección de certificados utilizando datos leídos de la InputStreaminstancia proporcionada . Si la secuencia dada no admite mark()y reset(), el método consumirá toda la secuencia.
  • public final CRL generateCRL(InputStream inputstream)crea una instancia y devuelve una CRL utilizando los datos leídos de la InputStreaminstancia proporcionada . Si la secuencia contiene más de una CRL y admite las operaciones mark()y reset(), el método leerá una CRL y dejará la secuencia antes de la siguiente.
  • public final Collection generateCRLs(InputStream inputstream)crea una instancia y devuelve una colección de CRL utilizando los datos leídos de la InputStreaminstancia proporcionada . Si la transmisión dada no admite mark()y reset(), public final Collection generateCRLs(InputStream inputstream)consumirá toda la transmisión.

Es importante comprender cómo se comportan esos cuatro métodos al generar instancias X.509 a partir de un flujo de datos. Vamos a ver.

Los métodos generateCertificate()y generateCRL()esperan que el contenido del flujo de entrada contenga representaciones codificadas en DER de un certificado o una CRL, respectivamente.

Ambos métodos generateCertificates()y generateCRLs()esperan que el contenido del flujo de entrada contenga una secuencia de representaciones codificadas en DER o un certificado o conjunto de CRL que cumpla con PKCS # 7 (Estándar de criptografía de clave pública # 7). (Ver Recursos para enlaces).

java.security.cert.Certificate

java.security.cert.Certificatedefine la interfaz común a todos los tipos de certificados: X.509, PGP y algunos otros. Los métodos más importantes de esta clase son:

  • public abstract PublicKey getPublicKey() devuelve la clave pública relacionada con la instancia de certificado en la que se llama a este método.
  • public abstract byte [] getEncoded() devuelve la forma codificada de ese certificado.
  • public abstract void verify(PublicKey publickey)y public abstract void verify(PublicKey publickey, String stringProvider)verificar que la clave privada correspondiente a la clave pública suministrada firmó el certificado en cuestión. Si las claves no coinciden, ambos métodos arrojan un SignatureException.

java.security.cert.X509Certificate

La clase java.security.cert.X509Certificateextiende la Certficateclase descrita anteriormente y agrega funcionalidad específica de X.509. Esta clase es importante porque normalmente interactúa con certificados en este nivel, no como la clase base.

  • public abstract byte [] getEncoded()devuelve la forma codificada de ese certificado, como arriba. El método utiliza la codificación DER para el certificado.

La mayor parte de java.security.cert.X509Certificatela funcionalidad adicional de 'consiste en métodos de consulta que devuelven información sobre el certificado. Presenté la mayor parte de esa información en la Parte 1. Estos son los métodos:

  • public abstract int getVersion() devuelve la versión del certificado.
  • public abstract Principal getSubjectDN() devuelve información que identifica al sujeto del certificado.
  • public abstract Principal getIssuerDN() devuelve información que identifica al emisor del certificado, que suele ser la CA, pero puede ser el sujeto si el certificado está autofirmado.
  • public abstract Date getNotBefore()y public abstract Date getNotAfter()valores de retorno que restringen el período de tiempo en el que el emisor está dispuesto a responder por la clave pública del sujeto.
  • public abstract BigInteger getSerialNumber()devuelve el número de serie del certificado. La combinación del nombre del emisor de un certificado y el número de serie es su identificación única. Ese hecho es crucial para la revocación de certificados, que discutiré con más detalle el próximo mes.
  • public abstract String getSigAlgName()y public abstract String getSigAlgOID()devolver información sobre el algoritmo utilizado para firmar el certificado.

Los siguientes métodos devuelven información sobre las extensiones definidas para el certificado. Recuerde, las extensiones son mecanismos para asociar información con un certificado; solo aparecen en los certificados de la versión 3.

  • public abstract int getBasicConstraints()devuelve la longitud de la ruta de restricciones de un certificado desde la BasicConstraintsextensión, si está definida. La ruta de restricciones especifica el número máximo de certificados de CA que pueden seguir este certificado en una ruta de certificación.
  • public abstract boolean [] getKeyUsage()devuelve el propósito del certificado codificado en la KeyUsageextensión.
  • public Set getCriticalExtensionOIDs()y public Set getNonCriticalExtensionOIDs()devuelve una colección de identificadores de objetos (OID) para las extensiones marcadas como críticas y no críticas, respectivamente. Un OID es una secuencia de números enteros que identifica universalmente un recurso.

No quiero dejarlos sin código para jugar, así que en lugar de profundizar en las CRL, que es un tema completo en sí mismo, presentaré el código y dejaré las CRL para la Parte 3.

El código

La siguiente clase demuestra cómo obtener una fábrica de certificados, cómo usar esa fábrica para generar un certificado a partir de la representación codificada en DER en un archivo y cómo extraer y mostrar información sobre el certificado. Notarás lo poco que tienes que preocuparte por la codificación subyacente.