The Gmail S/MIME API provides programmatic access to manage S/MIME email certificates for users in a Google Workspace 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:
- 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.
- 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)