Newer
Older
XinYang_IOS / Pods / OpenVPNAdapter / Sources / OpenVPN3 / openvpn / openssl / pki / x509certinfo.hpp
@zhangfeng zhangfeng on 7 Dec 2023 6 KB 1.8.0
//    OpenVPN -- An application to securely tunnel IP networks
//               over a single port, with support for SSL/TLS-based
//               session authentication and key exchange,
//               packet encryption, packet authentication, and
//               packet compression.
//
//    Copyright (C) 2012-2020 OpenVPN Inc.
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Affero General Public License Version 3
//    as published by the Free Software Foundation.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Affero General Public License for more details.
//
//    You should have received a copy of the GNU Affero General Public License
//    along with this program in the COPYING file.
//    If not, see <http://www.gnu.org/licenses/>.
//
//
//  Generic functions for extracting X.509 Certificate info from
//  OpenSSL X509 objects

#pragma once

#include <cstring>
#include <string>
#include <vector>

#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/x509v3.h>
#include <openssl/x509.h>

#include "openvpn/common/hexstr.hpp"
#include "openvpn/common/uniqueptr.hpp"

namespace openvpn {
namespace OpenSSLPKI {

/**
 *  Retrieve the complete X.509 Certificate Subject field
 *
 *  OpenSSL supports two ways of representing the subject line.  The old
 *  format is deprecated, but there might be code expecting this old format.
 *  The old format looks like this:
 *
 *      /C=KG/ST=NA/O=OpenVPN-TEST/CN=Test-Server/emailAddress=me@myhost.mydomain
 *
 *  The new format is UTF-8 compliant and has a different formatting scheme:
 *
 *      C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Server,
 *emailAddress=me@myhost.mydomain
 *
 *
 * @param cert         Pointer to a native OpenSSL X509 object containing the
 *                     certificate
 * @param new_format   (optional, default: false) Which format to use,
 *                     true indicates the new format
 *
 * @return Returns a std::string containing the complete certificate subject.
 *         If it was not possible to retrieve the subject, and empty string
 *         is returned.
 */
static std::string x509_get_subject(::X509 *cert, bool new_format = false) {
  if (!new_format) {
    unique_ptr_del<char> subject(
        X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0),
        [](char *p) { OPENSSL_free(p); });
    if (subject)
      return std::string(subject.get());
    else
      return std::string("");
  }

  unique_ptr_del<BIO> subject_bio(BIO_new(BIO_s_mem()),
                                  [](BIO *p) { BIO_free(p); });
  if (subject_bio == nullptr) {
    return std::string("");
  }

  X509_NAME_print_ex(subject_bio.get(), X509_get_subject_name(cert), 0,
                     XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN |
                         ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_CTRL);
  if (BIO_eof(subject_bio.get())) {
    return std::string("");
  }

  BUF_MEM *subject_mem = nullptr;
  BIO_get_mem_ptr(subject_bio.get(), &subject_mem);
  return std::string(subject_mem->data,
                     subject_mem->data + subject_mem->length);
}

/**
 * Retrives the algorithm used to sign a X509 certificate
 * @param cert 	OpenSSL certificate
 * @return
 */
static const std::string x509_get_signature_algorithm(const ::X509* cert)
{
  int nid = X509_get_signature_nid(cert);
  const char *sig = OBJ_nid2sn(nid);

  if (sig)
    {
      return sig;
    }
  else
    return "(error getting signature algorithm)";
}

/**
 *  Retrieves a specific portion of the X.509 Certificate subject field
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 * @param nid      Subject name ID to retrieve.  See openssl/obj_mac.h for
 *                 list of valid NID_* references.
 *
 * @return Returns the contents of the extracted field on success.  The
 *         resulting string may be empty if the extraction failed or the field
 *         is empty.
 */
static std::string x509_get_field(::X509 *cert, const int nid) {
  static const char nullc = '\0';
  std::string ret;
  X509_NAME *x509_name = X509_get_subject_name(cert);
  int i = X509_NAME_get_index_by_NID(x509_name, nid, -1);
  if (i >= 0) {
    X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
    if (ent) {
      ASN1_STRING *val = X509_NAME_ENTRY_get_data(ent);
      unsigned char *buf;
      buf = (unsigned char *)1;  // bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8
                                 // requires this workaround
      const int len = ASN1_STRING_to_UTF8(&buf, val);
      if (len > 0) {
        if (std::strlen((char *)buf) == static_cast<unsigned int>(len)) ret = (char *)buf;
        OPENSSL_free(buf);
      }
    }
  } else {
    i = X509_get_ext_by_NID(cert, nid, -1);
    if (i >= 0) {
      X509_EXTENSION *ext = X509_get_ext(cert, i);
      if (ext) {
        BIO *bio = BIO_new(BIO_s_mem());
        if (bio) {
          if (X509V3_EXT_print(bio, ext, 0, 0)) {
            if (BIO_write(bio, &nullc, 1) == 1) {
              char *str;
              const long len = BIO_get_mem_data(bio, &str);
              if (std::strlen(str) == static_cast<size_t>(len)) ret = str;
            }
          }
          BIO_free(bio);
        }
      }
    }
  }
  return ret;
}

/**
 *  Retrieves the X.509 certificate serial number
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 *
 * @return Returns the numeric representation of the certificate serial number
 *         as a std::string.
 */
static std::string x509_get_serial(::X509 *cert) {
  ASN1_INTEGER *asn1_i;
  BIGNUM *bignum;
  char *openssl_serial;

  asn1_i = X509_get_serialNumber(cert);
  bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
  openssl_serial = BN_bn2dec(bignum);

  const std::string ret = openssl_serial;

  BN_free(bignum);
  OPENSSL_free(openssl_serial);

  return ret;
}

/**
 *  Retrieves the X.509 certificate serial number as hexadecimal
 *
 * @param cert     Pointer to a native OpenSSL X509 object containing the
 *                 certificate
 *
 * @return Returns the hexadecimal representation of the certificate
 *         serial number as a std::string.
 */
static std::string x509_get_serial_hex(::X509 *cert) {
  const ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert);
  return render_hex_sep(asn1_i->data, asn1_i->length, ':', false);
}

}  // namespace OpenSSLPKI
}  // namespace openvpn