
es.gob.afirma.plugin.certvalidation.validation.OcspHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of afirma-ui-simpleafirma-plugin-validatecerts Show documentation
Show all versions of afirma-ui-simpleafirma-plugin-validatecerts Show documentation
Plugin para la validacion del certificado utilizado para firmar
The newest version!
/* Copyright (C) 2011 [Gobierno de Espana]
* This file is part of "Cliente @Firma".
* "Cliente @Firma" is free software; you can redistribute it and/or modify it under the terms of:
* - the GNU General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later version.
* - or The European Software License; either version 1.1 or (at your option) any later version.
* You may contact the copyright holder at: [email protected]
*/
package es.gob.afirma.plugin.certvalidation.validation;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.DERIA5String;
import org.spongycastle.asn1.DEROctetString;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x509.AccessDescription;
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
import org.spongycastle.asn1.x509.AuthorityInformationAccess;
import org.spongycastle.asn1.x509.Extension;
import org.spongycastle.asn1.x509.GeneralName;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.jcajce.JcaX509CertificateHolder;
import org.spongycastle.cert.ocsp.BasicOCSPResp;
import org.spongycastle.cert.ocsp.CertificateID;
import org.spongycastle.cert.ocsp.CertificateStatus;
import org.spongycastle.cert.ocsp.OCSPException;
import org.spongycastle.cert.ocsp.OCSPReqBuilder;
import org.spongycastle.cert.ocsp.OCSPResp;
import org.spongycastle.cert.ocsp.RespID;
import org.spongycastle.cert.ocsp.RevokedStatus;
import org.spongycastle.cert.ocsp.SingleResp;
import org.spongycastle.cert.ocsp.UnknownStatus;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.DigestCalculator;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import es.gob.afirma.core.misc.AOUtil;
/** Utilidades varias para el servicio OCSP.
* Clase cedida por YoHago.
* @author Tomás García-Merás */
final class OcspHelper {
static {
Security.addProvider(new BouncyCastleProvider());
}
private OcspHelper() {
// No permitimos la instanciacion
}
private static class Sha1DigestCalculator implements DigestCalculator {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private final MessageDigest digest;
Sha1DigestCalculator() throws NoSuchAlgorithmException {
this.digest = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
}
@Override
public OutputStream getOutputStream() {
return this.baos;
}
@Override
public byte[] getDigest() {
final byte[] bytes = this.digest.digest(this.baos.toByteArray());
this.baos.reset();
return bytes;
}
@Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return RespID.HASH_SHA1;
}
}
/** Obtiene una entrada a una clave privada de un almacén en formato PKCS#12 / PFX.
* @param pfxFile Archivo PKCS#12 / PFX.
* @param pfxPassword Contraseña del archivo PKCS#12 / PFX.
* @param alias Alias del certificado a usar.
* @return Entrada a una clave privada.
* @throws KeyStoreException Si hay problemas en el tratamiento del almacén de claves.
* @throws NoSuchAlgorithmException Si no se soporta algún algoritmo necesario.
* @throws CertificateException Si hay problemas tratando los certificados.
* @throws IOException Si hay problemas en el tratamiento de datos.
* @throws UnrecoverableEntryException Si una entrada del almacén de claves es inaccesible. */
static PrivateKeyEntry getSignData(final String pfxFile,
final String pfxPassword,
final String alias) throws KeyStoreException,
NoSuchAlgorithmException,
CertificateException,
IOException,
UnrecoverableEntryException {
if (pfxFile == null) {
throw new IllegalArgumentException("Debe indicarse un nombre de almacen PKCS#12"); //$NON-NLS-1$
}
if (pfxPassword == null) {
throw new IllegalArgumentException("Debe indicarse una contrasena para el almacen PKCS#12"); //$NON-NLS-1$
}
if (alias == null) {
throw new IllegalArgumentException("Debe indicarse un alias de certificado contenido en el almacen PKCS#12"); //$NON-NLS-1$
}
final KeyStore ks = KeyStore.getInstance("PKCS12"); //$NON-NLS-1$
ks.load(OcspHelper.class.getResourceAsStream(pfxFile), pfxPassword.toCharArray());
if (!ks.containsAlias(alias)) {
throw new IllegalArgumentException(
"El almacen proporcionado no contiene ninguna entrada con el alias: " + alias //$NON-NLS-1$
);
}
return (PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(pfxPassword.toCharArray()));
}
/** Envía una solicitud OSCP al responder.
* @param responderUrl URL del servidor OCSP.
* @param ocspRequest Solicitud OCSP a enviar.
* @return Respuesta del servidor OCSP.
* @throws IOException Si hay problemas en el tratamiento de datos o en la red. */
static byte[] sendOcspRequest(final URL responderUrl, final byte[] ocspRequest) throws IOException {
if (responderUrl == null) {
throw new IllegalArgumentException("La URL del servicio OCSP no puede ser nula"); //$NON-NLS-1$
}
if (ocspRequest == null) {
throw new IllegalArgumentException("La peticion OCSP no puede ser nula"); //$NON-NLS-1$
}
final HttpURLConnection conn = (HttpURLConnection) responderUrl.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST"); //$NON-NLS-1$
conn.setRequestProperty("Content-Type", "application/ocsp-request"); //$NON-NLS-1$//$NON-NLS-2$
conn.setRequestProperty("Accept", "application/ocsp-response"); //$NON-NLS-1$//$NON-NLS-2$
try (
final OutputStream out = conn.getOutputStream();
final DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
) {
dataOut.write(ocspRequest);
}
if (conn.getResponseCode() / 100 != 2) {
throw new IOException("El servidor OCSP ha devuelto un codigo de error " + conn.getResponseCode()); //$NON-NLS-1$
}
try (
final InputStream in = (InputStream) conn.getContent();
) {
return AOUtil.getDataFromInputStream(in);
}
}
/** Crea una solicitud OCSP.
* @param certToValidate Certificado a validar.
* @param issuerCert Certificado del emisor del certificado.
* @return Solicitud en ASN.1 binario.
* @throws OCSPException Si hay problemas accediendo al servicio OCSP.
* @throws NoSuchAlgorithmException Si no se soporta algún algoritmo necesario.
* @throws CertificateEncodingException Si hay problemas en el tratamiento de los certificados.
* @throws IOException Si hay problemas en el tratamiento de datos. */
static byte[] createOcspRequest(final X509Certificate certToValidate,
final X509Certificate issuerCert) throws CertificateEncodingException,
NoSuchAlgorithmException,
OCSPException,
IOException {
final CertificateID certId = new CertificateID(
new Sha1DigestCalculator(),
new JcaX509CertificateHolder(issuerCert != null ? issuerCert : certToValidate),
certToValidate.getSerialNumber()
);
final OCSPReqBuilder ocspRequestBuilder = new OCSPReqBuilder();
ocspRequestBuilder.addRequest(certId);
return ocspRequestBuilder.build().getEncoded();
}
/** Crea una solicitud OCSP firmada.
* @param certToValidate Certificado a validar.
* @param issuerCert Certificado del emisor del certificado.
* @param requestSignKey Entrada a la clave privada para la firma de la solicitud.
* @return Solicitud en ASN.1 binario.
* @throws CertificateEncodingException Si hay problemas en el tratamiento de los certificados.
* @throws NoSuchAlgorithmException Si no se soporta algún algoritmo necesario.
* @throws OCSPException Si hay problemas accediendo al servicio OCSP.
* @throws OperatorCreationException Si hay problemas creando los comandos OCSP.
* @throws IOException Si hay problemas en el tratamiento de datos. */
static byte[] createSignedOcspRequest(final X509Certificate certToValidate,
final X509Certificate issuerCert,
final PrivateKeyEntry requestSignKey) throws CertificateEncodingException,
NoSuchAlgorithmException,
OCSPException,
OperatorCreationException,
IOException {
final CertificateID certId = new CertificateID(
new Sha1DigestCalculator(),
new JcaX509CertificateHolder(issuerCert),
certToValidate.getSerialNumber()
);
final OCSPReqBuilder ocspRequestBuilder = new OCSPReqBuilder();
ocspRequestBuilder.addRequest(certId);
ocspRequestBuilder.setRequestorName(
new X500Name(
((X509Certificate)requestSignKey.getCertificate()).getSubjectX500Principal().toString()
)
);
return ocspRequestBuilder.build(
new JcaContentSignerBuilder("SHA1withRSA").build( //$NON-NLS-1$
requestSignKey.getPrivateKey()
),
new X509CertificateHolder[] {
new JcaX509CertificateHolder((X509Certificate) requestSignKey.getCertificate())
}
).getEncoded();
}
/** Analiza una respuesta OCSP.
*
* OCSPResponse ::= SEQUENCE {
* responseStatus OCSPResponseStatus,
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL
* }
*
* OCSPResponseStatus ::= ENUMERATED {
* successful (0), --Response has valid confirmations
* malformedRequest (1), --Illegal confirmation request
* internalError (2), --Internal error in issuer
* tryLater (3), --Try again later
* --(4) is not used
* sigRequired (5), --Must sign the request
* unauthorized (6) --Request unauthorized
* }
*
* @param resp Respuesta OCSP.
* @return Resultado de la validación según la respuesta OCSP.
* @throws IOException Si hay problemas en el tratamiento de datos.
* @throws OCSPException Si hay problemas accediendo al servicio OCSP. */
static ValidationResult analyzeOcspResponse(final byte[] resp) throws OCSPException, IOException {
if (resp == null) {
throw new IOException("La respuesta OCSP es nula"); //$NON-NLS-1$
}
final OCSPResp ocspResponse = new OCSPResp(resp);
if (ocspResponse.getStatus() == OCSPResp.SUCCESSFUL) {
final SingleResp[] responses = ((BasicOCSPResp) ocspResponse.getResponseObject()).getResponses();
final CertificateStatus certificateStatus = responses[0].getCertStatus();
if (certificateStatus == CertificateStatus.GOOD) {
return ValidationResult.VALID;
}
if (certificateStatus instanceof RevokedStatus) {
return ValidationResult.REVOKED;
}
if (certificateStatus instanceof UnknownStatus) {
return ValidationResult.UNKNOWN;
}
throw new IllegalArgumentException("La validacion ha devuelto una respuesta desconocida: " + certificateStatus.getClass().getName()); //$NON-NLS-1$
}
if (ocspResponse.getStatus() == OCSPResp.UNAUTHORIZED) {
return ValidationResult.UNAUTHORIZED;
}
if (ocspResponse.getStatus() == OCSPResp.INTERNAL_ERROR || ocspResponse.getStatus() == OCSPResp.TRY_LATER) {
return ValidationResult.SERVER_ERROR;
}
if (ocspResponse.getStatus() == OCSPResp.MALFORMED_REQUEST) {
return ValidationResult.MALFORMED_REQUEST;
}
if (ocspResponse.getStatus() == OCSPResp.SIG_REQUIRED) {
return ValidationResult.SIG_REQUIRED;
}
throw new IllegalArgumentException("La validacion ha devuelto un estado desconocido: " + ocspResponse.getStatus()); //$NON-NLS-1$
}
static List getAIALocations(final X509Certificate cert) throws IOException {
final byte[] aiaExtensionValue = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
if (aiaExtensionValue == null) {
return new ArrayList<>(0);
}
try (
final ASN1InputStream asn1In = new ASN1InputStream(aiaExtensionValue);
) {
final DEROctetString aiaDEROctetString = (DEROctetString) asn1In.readObject();
try (
final ASN1InputStream asn1InOctets = new ASN1InputStream(aiaDEROctetString.getOctets());
) {
final ASN1Sequence aiaASN1Sequence = (ASN1Sequence) asn1InOctets.readObject();
final AuthorityInformationAccess authorityInformationAccess = AuthorityInformationAccess.getInstance(aiaASN1Sequence);
final List ocspUrlList = new ArrayList<>();
final AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions();
for (final AccessDescription accessDescription : accessDescriptions) {
final GeneralName gn = accessDescription.getAccessLocation();
if (gn.getTagNo() == GeneralName.uniformResourceIdentifier) {
final DERIA5String str = DERIA5String.getInstance(gn.getName());
final String accessLocation = str.getString();
ocspUrlList.add(accessLocation);
}
}
if (ocspUrlList.isEmpty()) {
return new ArrayList<>(0);
}
return ocspUrlList;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy