This document describes the integration design between Vets.gov and the Veteran ID Card (VIC) service, for the purposes of allowing verified veterans to obtain a physical ID card.
Vets.gov will provide an entry point for the ID card workflow; when a verified veteran starts the workflow, Vets.gov will gather the required data attributes about that veteran and send a signed request to VIC.
VIC will accept the request from Vets.gov as a trusted source for veteran status and other identifiers. VIC will then provide a web interface allowing users to upload a photo, provide some additional data, and correct some of the data provided by Vets.gov/VA that is known to be unreliable (specifically address data).
VIC will then integrate with a third-party (currently Office Depot) to manage the actual printing and delivery of the physical ID card.
VIC service requires the following baseline data fields:
"edipi": "123456",
"firstname": "John",
"lastname": "Von Whitespace",
"address": "5050 someplace",
"city": "testCity",
"state": "WV",
"zip": "26505",
"email": "foo@example.com",
"phone": "",
"title38status": "V1",
"branchofservice": "AF",
"dischargetype": "A"
The above parameters are the minimum required to proceed with the ID Card request. They will be provided via a POST request with a typical form-encoded payload. Additionally, in order to avoid request forgery, some additional parameters will be included in the payload (see "Signing" below).
- Allowed values for
title38status
:V1, V2, V3, V4, V5, V6, V7
. Values/interpretation provied by VADIR service. - Allowed values for
branchofservice
:ARMY, NAVY, AF, PHS, MC, CG, NOAA
. These require translation from the values we retrieve from eMIS. branchofservice
may be a list, reflecting that a veteran served in multiple branches. If so it will be passed as a comma-delimited string.- Allowed values for
dischargetype
:A,B,D,E,F,H,J,K,Y,Z
. These values are passed exactly as returned from eMIS. dischargetype
may be a list, reflecting that a veteran may have had different discharge types from multiple service episodes. If so it will be passed as a comma-delimited string.
Vets.gov is able to initiate the ID Card workflow because it is a trusted entity that has required a user to log in and has verified the user's veteran status. In order to avoid other entities forging a request to VIC to initiate an ID card request, Vets.gov will sign each request using the following mechanism:
- Vets.gov will provide a public key/certificate
pubkey
to VIC, either offline or at a well-defined URL. - Vets.gov will maintain the corresponding private key
privkey
. - Vets.gov will calculate a
timestamp
for the request, to be represented in any JSON object as a string in ISO-8601 format. - For each request to VIC, Vets.gov will generate a canonical string representation of a JSON object containing the following attributes and their values in the following order:
edipi, firstname, lastname, address, city, state, zip, email, phone, branchofservice, timestamp
. The canonical string representation should not contain any whitespace or linebreaks between elements. Values should be in their original form, not URL-encoded. For example:
{"edipi":"123456","firstname":"John","lastname":"Von Whitespace","address":"5050 someplace","city":"testCity","state":"WV","zip":"26505","email":"foo@example.com","phone":"","title38status":"V1","branchofservice":"AF","dischagetype":"A","timestamp":"2017-08-17T16:23:40Z"}
- Vets.gov will calculate a signature
signature
for the request data by calculating a SHA256 digest of the canonical string representation, signing it withprivkey
, and base64-encoding the value using the URL-safe base64 encoding described in [RFC 4648] (https://tools.ietf.org/html/rfc4648#section-5). - Vets.gov will include
timestamp
andsignature
in the request parameters when making a request to VIC.
- Upon receiving the JSON request payload, VIC should re-create the canonical string representation using the same list of attributes/values in the same order as described above (i.e. by omitting the
signature
attribute and ensuring the attribute order is otherwise preserved, and URL-decoding all parameter values). - VIC can then verify the signature using the Vets.gov public key, the canonical string representation, and the base64-decoded signature provided in the request payload.
- VIC may additionally verify the timestamp of the request is within some threshold from the current time, in order to avoid accepting replayed requests.
The following command-line scripts demonstrate payload siging/verification in Ruby
Sign an exemplar JSON payload using a private key and output the canonical string, Base64-encoded signature, and URL-encoded query string:
require 'oj'
require 'openssl'
require 'base64'
require 'uri'
data = {
"edipi" => "123456",
"firstname" => "John",
"lastname" => "Von Whitespace",
"address" => "5050 someplace",
"city" => "testCity",
"state" => "WV",
"zip" => "26505",
"email" => "foo@example.com",
"phone" => "",
"title38status" => "V1",
"branchofservice" => "AF",
"dischargetype" => "A"
}
k = OpenSSL::PKey::RSA.new(File.read("vic_key.pem"))
data["timestamp"] = Time.now.utc.iso8601
data_str = Oj.dump(data)
digest = OpenSSL::Digest::SHA256.new
signature = k.sign(digest,data_str)
sig_str = Base64.urlsafe_encode64(signature)
data["signature"] = sig_str
puts "Canonical String:"
puts data_str
puts "Signature:"
puts sig_str
puts "Query String:"
puts URI.encode_www_form(data)
Verify a signature against an exemplar JSON payload using a public key, and output true/fase:
require 'oj'
require 'openssl'
require 'base64'
require 'uri'
data = URI.decode_www_form(ARGV[0]).to_h
signature = data.delete("signature")
signature = Base64.urlsafe_decode64(signature)
c = OpenSSL::X509::Certificate.new(File.read("vic_cert.pem"))
pubkey = c.public_key
data_str = Oj.dump(data)
digest = OpenSSL::Digest::SHA256.new
#signature = Base64.urlsafe_decode64(ARGV[0])
puts pubkey.verify(digest, signature, data_str)
Canonical String:
{"edipi":"123456","firstname":"John","lastname":"Von Whitespace","address":"5050 someplace","city":"testCity","state":"WV","zip":"26505","email":"foo@example.com","phone":"","title38status":"V1","branchofservice":"AF","dischargetype":"A","timestamp":"2017-09-25T20:43:02Z"}
Signature (URL-safe Base64-encoded):
GRiMCS2rr_Xx_fOq-2TDof7-FW2tZC-Z3RtOPI94h4ZNaML1jiB2IyJXuEHA8Z5E7lIkXyPMC-VmqnIsxOyBz1-2tmDOLd8OkgNe1FdyKIeVoNwK2DGTKJwpc7cnPl-zHqW9z9HqnHf0vTjkQ_s4eZicAArXB6K63u3waEKomH3NTniK6MYjaFOZ99cjNPpZZ_LV2105cxvGUHdYI9ErPnAoF0ZBR7ntSObs7T7J-ZA7vg_c6wyzm3FrVdqGb8mtWSEb-RyY0waTpbayco_pBP1zGeVRpXhE_yiBYHc62ormTgwTxAgC2Spm0vcLuqs_kxI0kpYtwAwLGXveD6dpHA==
URL Query String (all values additionally URL-encoded):
edipi=123456&firstname=John&lastname=Von+Whitespace&address=5050+someplace&city=testCity&state=WV&zip=26505&email=foo%40example.com&phone=&title38status=V1&branchofservice=AF&dischargetype=A×tamp=2017-09-25T20%3A43%3A02Z&signature=GRiMCS2rr_Xx_fOq-2TDof7-FW2tZC-Z3RtOPI94h4ZNaML1jiB2IyJXuEHA8Z5E7lIkXyPMC-VmqnIsxOyBz1-2tmDOLd8OkgNe1FdyKIeVoNwK2DGTKJwpc7cnPl-zHqW9z9HqnHf0vTjkQ_s4eZicAArXB6K63u3waEKomH3NTniK6MYjaFOZ99cjNPpZZ_LV2105cxvGUHdYI9ErPnAoF0ZBR7ntSObs7T7J-ZA7vg_c6wyzm3FrVdqGb8mtWSEb-RyY0waTpbayco_pBP1zGeVRpXhE_yiBYHc62ormTgwTxAgC2Spm0vcLuqs_kxI0kpYtwAwLGXveD6dpHA%3D%3D
Can the boolean attributes be normalized to standard JSONYes.true/false
values instead of"Y"/"N"
strings?Confirm that VIC implements any required business logic around tracking history of ID card requests - i.e. not letting a given veteran make an unlimited number of requests.Confirmed.
- 2017-08-17:
retired
andserviceconnected
were removed from the official parameter list. Note they may still be present in some of the example code. - 2017-09-25: Added
dischargetype
to list of parameters. - 2017-11-30: Added
title38status
(has been integrated in code for a while, this document was out of date).