Managing S/MIME Certificates

The Gmail S/MIME API provides programmatic access to manage S/MIME email certificates for users in a G Suite domain.

An administrator must enable S/MIME for the domain in order for the certificates to work.

The S/MIME standard provides a specification for public key encryption and signing of MIME data. Configuring S/MIME certificates in a user's account causes Gmail to use that certificate in the following ways:

  • Gmail uses the user's certificate and private key to sign outgoing mail.
  • Gmail uses the user's private key to decrypt incoming mail.
  • Gmail uses the recipient's certificate and public key to encrypt outgoing mail.
  • Gmail uses the sender's certificate and public key to verify incoming mail.

You generate individual S/MIME certificates and upload them using the API. Each S/MIME certificate is for a specific alias for a user email account. Aliases include the primary email address as well as custom "Send As" addresses. A single S/MIME cert is marked as the default for each alias.

Authorizing API access

There are two forms of authorizing access to the API:

  1. You can use a service account with domain-wide access. This must be enabled in the Domain control panel by a Domain Admin. Your service account can then retrieve credentials for every user in the domain, and use these credentials to access the API.
  2. You can use standard three-legged authorization — which requires end-user consent — to get an Oauth2 access token. To access the API in this way, the Domain admin must enable the "S/MIME API end user access enabled" checkbox in the Domain control panel.

ACL scopes

This API relies on the same ACL scopes as the Gmail sendAs methods:

gmail.settings.basic
This scope is required for updating the primary SendAs S/MIME.
gmail.settings.sharing
This scope is required for updating custom from S/MIME.

Using the API

The users.settings.sendAs.smimeInfo resource provides the methods that you use to manage S/MIME certificates. Each certificate is associated with one send-as alias for a user.

Upload an S/MIME key

Use the smimeInfo.insert() method to upload a new S/MIME key for an alias belonging to a user. You identify the target alias using the following parameters:

userId
The user's email address. You can use the special value me to indicate the currently authenticated user.
sendAsEmail
The alias for which you're uploading the key. This is the email address that appears in the "From:" header for mail sent using this alias.

The S/MIME certificate and private key should be present in the pkcs12 field in that format; no other fields should be set in the request. The PKCS12 field is expected to contain both the user S/MIME key and the signing certificate chain. The API performs standard validations on this field before accepting it, verifying the following:

  • The subject matches the specified email address.
  • Expirations are valid.
  • The issuing certificate authority (CA) is in our trusted list.
  • The certificates match Gmail's technical constraints.

If the key is encrypted then the password should be in encryptedKeyPassword field. Successful insert() calls will return the smimeInfo's ID that can be used to refer to the key in the future.

List a user's S/MIME keys

Use the smimeInfo.list() method to return the list of S/MIME keys for the given user for the given alias. You identify the target alias using the following parameters:

userId
The user's email address. You can use the special value me to indicate the currently authenticated user.
sendAsEmail
The alias for which to list keys. This is the email address that appears in the "From:" header for mail sent using this alias.

Retrieve the S/MIME keys for an alias

Use the smimeInfo.get() method to return the specific S/MIME keys for a specific send-as alias for a user. You identify the target alias using the following parameters:

userId
The user's email address. You can use the special value me to indicate the currently authenticated user.
sendAsEmail
The alias for which you're retrieving the keys. This is the email address that appears in the "From:" header for mail sent using this alias.

Delete an S/MIME key

Use the smimeInfo.delete() method to delete the specified S/MIME key from an alias. You identify the target alias using the following parameters:

userId
The user's email address. You can use the special value me to indicate the currently authenticated user.
sendAsEmail
The alias for which you're retrieving the keys. This is the email address that appears in the "From:" header for mail sent using this alias.
id
The immutable ID for the SmimeInfo.

Set the default S/MIME key for an alias

Use the smimeInfo.setDefault() method to mark the specified S/MIME key as the default for the specified alias. You identify the target alias using the following parameters:

userId
The user's email address. You can use the special value me to indicate the currently authenticated user.
sendAsEmail
The alias for which you're retrieving the keys. This is the email address that appears in the "From:" header for mail sent using this alias.
id
The immutable ID for the SmimeInfo.

Sample code

The following code samples demonstrate using the API to manage S/MIME certificates for an organization with multiple users.

Creating an SmimeInfo resource for an S/MIME certificate

The following code sample demonstrates reading a certificate from file, encoding to a base64url string, and assigning it to the pkcs12 field of the smimeInfo resource:

Java

  /**
   * Create an SmimeInfo resource for a certificate from file.
   *
   * @param filename Name of the file containing the S/MIME certificate.
   * @param password Password for the certificate file, or null if the file is not
   *     password-protected.
   * @return An SmimeInfo object with the specified certificate.
   */
  public static SmimeInfo createSmimeInfo(String filename, String password) {
    SmimeInfo smimeInfo = null;
    InputStream in = null;

    try {
      File file = new File(filename);
      in = new FileInputStream(file);
      byte fileContent[] = new byte[(int) file.length()];
      in.read(fileContent);

      smimeInfo = new SmimeInfo();
      smimeInfo.setPkcs12(Base64.getUrlEncoder().encodeToString(fileContent));
      if (password != null && password.length() > 0) {
        smimeInfo.setEncryptedKeyPassword(password);
      }
    } catch (Exception e) {
      System.out.printf("An error occured while reading the certificate file: %s\n", e);
    } finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (IOException ioe) {
        System.out.printf("An error occured while closing the input stream: %s\n", ioe);
      }
    }

    return smimeInfo;
  }

Python

def create_smime_info(cert_filename, cert_password=None):
  """Create an smimeInfo resource for a certificate from file.

  Args:
    cert_filename: Name of the file containing the S/MIME certificate.
    cert_password: Password for the certificate file, or None if the file is not
        password-protected.
  """
  smime_info = None
  try:
    with open(cert_filename, 'r') as f:
      smime_info = {}
      data = f.read().encode('UTF-8')
      smime_info['pkcs12'] = base64.urlsafe_b64encode(data)
      if cert_password and len(cert_password) > 0:
        smime_info['encryptedKeyPassword'] = cert_password
  except (OSError, IOError) as error:
    print('An error occurred while reading the certificate file: %s' % error)

  return smime_info

Uploading an S/MIME certificate

To upload a certificate, call smimeInfo.insert, and supply the smimeInfo resource in the body of the request:

Java

  /**
   * Upload an S/MIME certificate for the user.
   *
   * @param service Authorized GMail API service instance.
   * @param userId User's email address.
   * @param sendAsEmail The "send as" email address, or null if it should be the same as userId.
   * @param smimeInfo The SmimeInfo object containing the user's S/MIME certificate.
   * @return An SmimeInfo object with details about the uploaded certificate.
   */
  public static SmimeInfo insertSmimeInfo(
      Gmail service, String userId, String sendAsEmail, SmimeInfo smimeInfo) {
    if (sendAsEmail == null) {
      sendAsEmail = userId;
    }

    try {
      SmimeInfo results =
          service
              .users()
              .settings()
              .sendAs()
              .smimeInfo()
              .insert(userId, sendAsEmail, smimeInfo)
              .execute();
      System.out.printf("Inserted certificate, id: %s\n", results.getId());
      return results;
    } catch (IOException e) {
      System.err.printf("An error occured: %s", e);
    }

    return null;
  }

Python

def insert_smime_info(service, user_id, smime_info, send_as_email=None):
  """Upload an S/MIME certificate for the user.

  Args:
    service: Authorized GMail API service instance.
    user_id: User's email address.
    smime_info: The smimeInfo resource containing the user's S/MIME certificate.
    send_as_email: The "send as" email address, or None if it should be the same
        as user_id.
  """
  if not send_as_email:
    send_as_email = user_id
  try:
    results = service.users().settings().sendAs().smimeInfo().insert(
        userId=user_id, sendAsEmail=send_as_email, body=smime_info).execute()
    print('Inserted certificate; id: %s' % results['id'])
    return results
  except errors.HttpError as error:
    print('An error occurred: %s' % error)
    return None

Examples for managing many users' certificates

You may want to manage certificates for many users in the organization at once. The following examples show how to manage certificates for multiple users in one batch call.

Inserting certificates from a CSV file

Suppose you have a CSV file that lists user IDs and the path to each user's certificate:

$ cat certificates.csv
user1@example.com,/path/to/user1_cert.p12,cert_password_1
user2@example.com,/path/to/user2_cert.p12,cert_password_2
user3@example.com,/path/to/user3_cert.p12,cert_password_3

Java

You can make use of the createSmimeInfo and insertSmimeInfo calls from earlier to upload the certs as specified in the CSV file:

  /**
   * A builder that returns a GMail API service instance that is authorized to act on behalf of the
   * specified user.
   */
  @FunctionalInterface
  public interface GmailServiceBuilder {
    Gmail buildGmailServiceFromUserId(String userId) throws IOException;
  }

  /**
   * Upload S/MIME certificates based on the contents of a CSV file.
   *
   * <p>Each row of the CSV file should contain a user ID, path to the certificate, and the
   * certificate password.
   *
   * @param serviceBuilder A function that returns an authorized GMail API service instance for a
   *     given user.
   * @param csvFilename Name of the CSV file.
   */
  public static void insertCertFromCsv(GmailServiceBuilder serviceBuilder, String csvFilename) {
    try {
      File csvFile = new File(csvFilename);
      CSVParser parser =
          CSVParser.parse(csvFile, java.nio.charset.StandardCharsets.UTF_8, CSVFormat.DEFAULT);
      for (CSVRecord record : parser) {
        String userId = record.get(0);
        String certFilename = record.get(1);
        String certPassword = record.get(2);
        SmimeInfo smimeInfo = createSmimeInfo(certFilename, certPassword);
        if (smimeInfo != null) {
          insertSmimeInfo(
              serviceBuilder.buildGmailServiceFromUserId(userId), userId, userId, smimeInfo);
        } else {
          System.err.printf("Unable to read certificate file for userId: %s\n", userId);
        }
      }
    } catch (Exception e) {
      System.err.printf("An error occured while reading the CSV file: %s", e);
    }
  }

Python

You can make use of the create_smime_info and insert_smime_info calls from earlier to upload the certs as specified in the CSV file:

def insert_cert_from_csv(service_builder, csv_filename):
  """Upload S/MIME certificates based on the contents of a CSV file.

  Each row of the CSV file should contain a user ID, path to the certificate,
  and the certificate password.

  Args:
    service_builder: A function that returns an authorized GMail API service
        instance for a given user.
    csv_filename: Name of the CSV file.
  """
  try:
    with open(csv_filename, 'r') as f:
      csv_reader = csv.reader(f, delimiter=',')
      next(csv_reader, None)  # skip CSV file header
      for row in csv_reader:
        user_id = row[0]
        cert_filename = row[1]
        cert_password = row[2]
        smime_info = create_smime_info(cert_filename, cert_password)
        if smime_info:
          insert_smime_info(service_builder(user_id), user_id, smime_info)
        else:
          print('Unable to read certificate file for user_id: %s' % user_id)
  except (OSError, IOError) as error:
    print('An error occured while reading the CSV file: %s' % error)

Certificate management

This example combines several calls from the smimeInfo API to show how you might manage certificates for your organization. It lists the certs for the user and if the default cert is expired or not set, it uploads the cert found in the specified file. Then it sets the cert whose expiration is furthest into the future as the default.

This is then called from a function that processes a CSV file as in the previous example.

Java

  /**
   * Update S/MIME certificates for the user.
   *
   * <p>First performs a lookup of all certificates for a user. If there are no certificates, or
   * they all expire before the specified date/time, uploads the certificate in the specified file.
   * If the default certificate is expired or there was no default set, chooses the certificate with
   * the expiration furthest into the future and sets it as default.
   *
   * @param service Authorized GMail API service instance.
   * @param userId User's email address.
   * @param sendAsEmail The "send as" email address, or None if it should be the same as user_id.
   * @param certFilename Name of the file containing the S/MIME certificate.
   * @param certPassword Password for the certificate file, or None if the file is not
   *     password-protected.
   * @param expireTime DateTime object against which the certificate expiration is compared. If
   *     None, uses the current time. @ returns: The ID of the default certificate.
   * @return The ID of the default certifcate.
   */
  public static String updateSmimeCerts(
      Gmail service,
      String userId,
      String sendAsEmail,
      String certFilename,
      String certPassword,
      LocalDateTime expireTime) {
    if (sendAsEmail == null) {
      sendAsEmail = userId;
    }

    ListSmimeInfoResponse listResults = null;
    try {
      listResults =
          service.users().settings().sendAs().smimeInfo().list(userId, sendAsEmail).execute();
    } catch (IOException e) {
      System.err.printf("An error occurred during list: %s\n", e);
      return null;
    }

    String defaultCertId = null;
    String bestCertId = null;
    LocalDateTime bestCertExpire = LocalDateTime.MIN;

    if (expireTime == null) {
      expireTime = LocalDateTime.now();
    }
    if (listResults != null && listResults.getSmimeInfo() != null) {
      for (SmimeInfo smimeInfo : listResults.getSmimeInfo()) {
        String certId = smimeInfo.getId();
        boolean isDefaultCert = smimeInfo.getIsDefault();
        if (isDefaultCert) {
          defaultCertId = certId;
        }
        LocalDateTime exp =
            LocalDateTime.ofInstant(
                Instant.ofEpochMilli(smimeInfo.getExpiration()), ZoneId.systemDefault());
        if (exp.isAfter(expireTime)) {
          if (exp.isAfter(bestCertExpire)) {
            bestCertId = certId;
            bestCertExpire = exp;
          }
        } else {
          if (isDefaultCert) {
            defaultCertId = null;
          }
        }
      }
    }
    if (defaultCertId == null) {
      String defaultId = bestCertId;
      if (defaultId == null && certFilename != null) {
        SmimeInfo smimeInfo = createSmimeInfo(certFilename, certPassword);
        SmimeInfo insertResults = insertSmimeInfo(service, userId, sendAsEmail, smimeInfo);
        if (insertResults != null) {
          defaultId = insertResults.getId();
        }
      }

      if (defaultId != null) {
        try {
          service
              .users()
              .settings()
              .sendAs()
              .smimeInfo()
              .setDefault(userId, sendAsEmail, defaultId)
              .execute();
          return defaultId;
        } catch (IOException e) {
          System.err.printf("An error occured during setDefault: %s", e);
        }
      }
    } else {
      return defaultCertId;
    }

    return null;
  }

  /**
   * Update S/MIME certificates based on the contents of a CSV file.
   *
   * <p>Each row of the CSV file should contain a user ID, path to the certificate, and the
   * certificate password.
   *
   * @param serviceBuilder A function that returns an authorized GMail API service instance for a
   *     given user.
   * @param csvFilename Name of the CSV file.
   * @param expireTime DateTime object against which the certificate expiration is compared. If
   *     None, uses the current time.
   */
  public static void updateSmimeFromCsv(
      GmailServiceBuilder serviceBuilder, String csvFilename, LocalDateTime expireTime) {
    try {
      File csvFile = new File(csvFilename);
      CSVParser parser =
          CSVParser.parse(
              csvFile,
              java.nio.charset.StandardCharsets.UTF_8,
              CSVFormat.DEFAULT.withHeader().withSkipHeaderRecord());
      for (CSVRecord record : parser) {
        String userId = record.get(0);
        String certFilename = record.get(1);
        String certPassword = record.get(2);
        updateSmimeCerts(
            serviceBuilder.buildGmailServiceFromUserId(userId),
            userId,
            userId,
            certFilename,
            certPassword,
            expireTime);
      }
    } catch (Exception e) {
      System.err.printf("An error occured while reading the CSV file: %s", e);
    }
  }

Python

def update_smime_certs(service,
                       user_id,
                       send_as_email=None,
                       cert_filename=None,
                       cert_password=None,
                       expire_dt=None):
  """Update S/MIME certificates for the user.

  First performs a lookup of all certificates for a user.  If there are no
  certificates, or they all expire before the specified date/time, uploads the
  certificate in the specified file.  If the default certificate is expired or
  there was no default set, chooses the certificate with the expiration furthest
  into the future and sets it as default.

  Args:
    service: Authorized GMail API service instance.
    user_id: User's email address.
    send_as_email: The "send as" email address, or None if it should be the same
        as user_id.
    cert_filename: Name of the file containing the S/MIME certificate.
    cert_password: Password for the certificate file, or None if the file is not
        password-protected.
    expire_dt: DateTime object against which the certificate expiration is
      compared.  If None, uses the current time.

  Returns:
    The ID of the default certificate.
  """
  if not send_as_email:
    send_as_email = user_id

  try:
    results = service.users().settings().sendAs().smimeInfo().list(
        userId=user_id, sendAsEmail=send_as_email).execute()
  except errors.HttpError as error:
    print('An error occurred during list: %s' % error)
    return None

  default_cert_id = None
  best_cert_id = (None, datetime.datetime.fromtimestamp(0))

  if not expire_dt:
    expire_dt = datetime.datetime.now()
  if results and 'smimeInfo' in results:
    for smime_info in results['smimeInfo']:
      cert_id = smime_info['id']
      is_default_cert = smime_info['isDefault']
      if is_default_cert:
        default_cert_id = cert_id
      exp = datetime.datetime.fromtimestamp(smime_info['expiration'] / 1000)
      if exp > expire_dt:
        if exp > best_cert_id[1]:
          best_cert_id = (cert_id, exp)
      else:
        if is_default_cert:
          default_cert_id = None

  if not default_cert_id:
    default_id = best_cert_id[0]
    if not default_id and cert_filename:
      smime_info = create_smime_info(cert_filename, cert_password)
      results = insert_smime_info(service, user_id, smime_info)
      if results:
        default_id = results['id']

    if default_id:
      try:
        service.users().settings().sendAs().smimeInfo().setDefault(
            userId=user_id, sendAsEmail=send_as_email, id=default_id).execute()
        return default_id
      except errors.HttpError as error:
        print('An error occurred during setDefault: %s' % error)
  else:
    return default_cert_id

  return None


def update_smime_from_csv(service_builder, csv_filename, expire_dt=None):
  """Update S/MIME certificates based on the contents of a CSV file.

  Each row of the CSV file should contain a user ID, path to the certificate,
  and the certificate password.

  Args:
    service_builder: A function that returns an authorized GMail API service
        instance for a given user.
    csv_filename: Name of the CSV file.
    expire_dt: DateTime object against which the certificate expiration is
      compared.  If None, uses the current time.
  """
  try:
    with open(csv_filename, 'r') as f:
      csv_reader = csv.reader(f, delimiter=',')
      next(csv_reader, None)  # skip CSV file header
      for row in csv_reader:
        user_id = row[0]
        update_smime_certs(
            service_builder(user_id),
            user_id,
            cert_filename=row[1],
            cert_password=row[2],
            expire_dt=expire_dt)
  except (OSError, IOError) as error:
    print('An error occured while reading the CSV file: %s' % error)

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。