blob: 8c55767f930c4763b8bd56750b9cd78600036a73 [file] [log] [blame]
package com.intellij.util.net.ssl;
import com.intellij.util.containers.HashMap;
import org.apache.commons.codec.digest.DigestUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.security.auth.x500.X500Principal;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
/**
* @author Mikhail Golubev
*/
@SuppressWarnings("UnusedDeclaration")
public class CertificateWrapper {
@NonNls public static final String NOT_AVAILABLE = "N/A";
private final X509Certificate myCertificate;
private final Map<String, String> myIssuerFields;
private final Map<String, String> mySubjectFields;
public CertificateWrapper(@NotNull X509Certificate certificate) {
myCertificate = certificate;
myIssuerFields = extractFields(certificate.getIssuerX500Principal());
mySubjectFields = extractFields(certificate.getSubjectX500Principal());
}
/**
* @param name - Common name of desired issuer field
* @return field value of {@link #NOT_AVAILABLE}. if it doesn't exist
*/
@NotNull
public String getIssuerField(@NotNull CommonField name) {
String field = myIssuerFields.get(name.getShortName());
return field == null ? NOT_AVAILABLE : field;
}
/**
* @param name - Common name of desired subject field
* @return field value of {@link #NOT_AVAILABLE}, if it doesn't exist
*/
@NotNull
public String getSubjectField(@NotNull CommonField name) {
String field = mySubjectFields.get(name.getShortName());
return field == null ? NOT_AVAILABLE : field;
}
/**
* Returns SHA-256 fingerprint of the certificate.
*
* @return SHA-256 fingerprint or {@link #NOT_AVAILABLE} in case of any error
*/
@NotNull
public String getSha256Fingerprint() {
try {
return DigestUtils.sha256Hex(myCertificate.getEncoded());
}
catch (CertificateEncodingException e) {
return NOT_AVAILABLE;
}
}
/**
* Returns SHA-1 fingerprint of the certificate.
*
* @return SHA-1 fingerprint or {@link #NOT_AVAILABLE} in case of any error
*/
public String getSha1Fingerprint() {
try {
return DigestUtils.sha1Hex(myCertificate.getEncoded());
}
catch (Exception e) {
return NOT_AVAILABLE;
}
}
/**
* Check whether certificate is valid. It's considered invalid it it either expired or
* not yet valid.
*
* @see #isExpired()
* @see #isNotYetValid()
*/
public boolean isValid() {
try {
myCertificate.checkValidity();
return true;
}
catch (Exception e) {
return false;
}
}
public boolean isExpired() {
return new Date().getTime() > myCertificate.getNotAfter().getTime();
}
public boolean isNotYetValid() {
return new Date().getTime() < myCertificate.getNotBefore().getTime();
}
/**
* Check whether certificate is self-signed. It's considered self-signed if
* its issuer and subject are the same.
*/
public boolean isSelfSigned() {
return myCertificate.getIssuerX500Principal().equals(myCertificate.getSubjectX500Principal());
}
public int getVersion() {
return myCertificate.getVersion();
}
@NotNull
public String getSerialNumber() {
return myCertificate.getSerialNumber().toString();
}
public X509Certificate getCertificate() {
return myCertificate;
}
public X500Principal getIssuerX500Principal() {
return myCertificate.getIssuerX500Principal();
}
public X500Principal getSubjectX500Principal() {
return myCertificate.getSubjectX500Principal();
}
public Date getNotBefore() {
return myCertificate.getNotBefore();
}
public Date getNotAfter() {
return myCertificate.getNotAfter();
}
public Map<String, String> getIssuerFields() {
return myIssuerFields;
}
public Map<String, String> getSubjectFields() {
return mySubjectFields;
}
// E.g. CN=*.github.com,O=GitHub\, Inc.,L=San Francisco,ST=California,C=US
private static Map<String, String> extractFields(X500Principal principal) {
Map<String, String> fields = new HashMap<String, String>();
for (String field : principal.getName().split("(?<!\\\\),")) {
String[] parts = field.trim().split("=", 2);
if (parts.length != 2) {
continue;
}
fields.put(parts[0], parts[1].replaceAll("\\\\,", ","));
}
return Collections.unmodifiableMap(fields);
}
@Override
public final boolean equals(Object other) {
return other instanceof CertificateWrapper && myCertificate.equals(((CertificateWrapper)other).getCertificate());
}
@Override
public final int hashCode() {
return myCertificate.hashCode();
}
/**
* Find out full list of names from specification.
* See http://tools.ietf.org/html/rfc5280#section-4.1.2.2 for details.
*/
public enum CommonField {
COMMON_NAME("CN", "Common Name"),
ORGANIZATION("O", "Organization"),
ORGANIZATION_UNIT("OU", "Organizational Unit"),
LOCATION("L", "Locality"),
COUNTRY("C", "Country"),
STATE("ST", "State or Province");
private final String myShortName;
private final String myLongName;
CommonField(@NotNull String shortName, @NotNull String longName) {
myShortName = shortName;
myLongName = longName;
}
public String getShortName() {
return myShortName;
}
public String getLongName() {
return myLongName;
}
}
}