Class PutDataCommand
Put a Data Object onto the YubiKey in a storage location.
public sealed class PutDataCommand : IYubiKeyCommand<PutDataResponse>
- Inheritance
-
objectPutDataCommand
- Implements
Remarks
The partner Response class is PutDataResponse.
See also the User's Manual entries on Get and Put Data and PIV objects.
A Data Object is a DataTag/Data pair. Think of it as a "key-value pair", with the DataTag a key and the Data a value. The DataTag is simply a number and associated with each is a definition of data elements and how they are encoded.
The PIV standard specifies a set of Data Objects. See a description of each DataTag and the associated Data in the User's Manual entry on the GET DATA command. Yubico specifies some non-standard DataTags as well. See descriptions in the User's Manual entry on Get and Put vendor data. The YubiKey also allows an application to use undefined DataTags as well. See the See the User's Manual entry on Data Objects for a table describing the undefined DataTags.
The caller supplies the DataTag and the data, this command will store that data on the YubiKey under the DataTag. If the YubiKey already contains information under the given DataTag, this command replaces it.
Note that for some Data Objects there are higher-level APIs that are easier to use. An application that needs to store information often will not need to use this command. For example, if you want to put a certificate onto a YubiKey, use ImportCertificate(byte, X509Certificate2, bool). Or if you want to store/retrieve Key History, use ReadObject<T>() and WriteObject(PivDataObject) along with the KeyHistory class. Under the covers, these APIs will ultimately call this command. But the application that uses the SDK can simply make the specific API calls, rather than use Put Data.
There are a number of ways to use this command. The old, obsolete way is
to provide the DataTag using the PivDataTag
enum, along with data
encoded as defined by PIV. The constructor
PutDataCommand(PivDataTag, ReadOnlyMemory<byte>)
and the
properties Tag
and EncodedData
require using PIV-defined
DataTags with PIV-defined encoded data only. This constructor and these
properties are marked "Obsolete" and will be removed from the SDK in the
future. However, it will still be possible to get the same functionality
using the updated API.
The API you should use are the constructors PutDataCommand()
, and
PutDataCommand(int, ReadOnlyMemory<byte>)
c>, along with the
properties DataTag
and Data
. Using these will allow you to
use any DataTag (not just those defined by PIV) and any data (not just
the data and encoding specified by PIV). There are two restrictions on
the data. One, each storage area on the YubiKey can only store up to
2,800 bytes, and two, the data must be encoded as
53 length
data
For example, to store 04 02 55 44 02 01 7F
the data must be provided as
53 07
04 02 55 44 02 01 7F
While you can store any data under a PIV-defined DataTag, if you want to
use only PIV-defined DataTags, and want to verify that the encoding
follows the PIV standard (as happens using the old, obsolete API), you
can use the PivDataTag
class. For example,
// Store IrisImages
if (PivDataTag.IrisImages.IsValidEncodingForPut(encodedDataToStore))
{
var putCmd = new PutDataCommand(
(int)PivDataTag.IrisImages, encodedDataToStore);
}
Note that even though it is possible, using this command, to store any
data under a PIV-defined DataTag, it is not recommended. If you have some
other data you would like to store, you can use an "undefined" DataTag.
For example, the YubiKey will store data under the number
0x005F0101
. That is a number not used by PIV and not used by
Yubico. But the YubiKey will accept it. So if you have non-PIV data you
want to store, rather than use a PIV-defined number, just use one of the
undefined values. See the User's Manual entry on
Data Objects
for tables describing possible DataTags.
There is probably only one reason to store non-PIV data under a PIV-defined DataTag, and that is to require the PIN to retrieve. There are four PIV-defined DataTags for which the PIN is required to retrieve the data: Printed, Iris, FacialImages, and Fingerprints. Data stored under any other DataTag can be retrieved by anyone with access to the YubiKey. If you want to store some data and want it to be accessible only with PIN verification, then you must store it under one of those DataTags. However, Yubico already stores data under the Printed DataTag. That data is needed to configure the YubiKey in "PIN-Protected mode". Hence, it would be a good idea to stay away from that DataTag.
This command will not allow putting data for all PIV data tags. The Discovery and BITGT DataTags will not be accepted. The Discovery Data object is fixed data and it would inhibit the YubiKey's ability to operate if that value were changed from what was installed at manufacture. In virtually all real-world applications, this will likely not be a problem.
Note that when you set an object with the DataTag or Data using either the old constructor/properties or the new versions, when you get it (using either old or new), you are getting the same thing. For example,
// Use the old, obsolete API to set the tag and data.
var putCmd = new PutDataCommand()
{
Tag = PivDataTag.KeyHistory,
EncodedData = encodedKeyHistory,
}
PivDataTag pivDataTag = putCmd.Tag;
int dataTag = putCmd.DataTag;
// At this point pivDataTag will equal PivDataTag.KeyHistory = 0x005FC10C
// dataTag will equal 0x005FC10C
// Even though the code used the old API to set the Tag property
// the new API DataTag property will return the same value.
This class will copy a reference to the data to put, so you should not
clear or alter that input data until this class is done with it, which is
after the call to SendCommand
.
Example:
/* This example assumes there is some code that will build an encoded
certificate from an X509Certificate2 object. */
byte[] encodedCertificate = PivPutDataEncodeCertificate(certObject);
IYubiKeyConnection connection = key.Connect(YubiKeyApplication.Piv);
PutDataCommand putDataCommand = new PutDataCommand(
(int)PivDataTag.Authentication, encodedCertificate);
PutDataResponse putDataResponse = connection.SendCommand(putDataCommand);
if (getDataResponse.StatusWord != SWConstants.Success)
{
/* handle case where the the PUT did not work. */
}
Constructors
PutDataCommand()
Initializes a new instance of the PutDataCommand
class.
public PutDataCommand()
Remarks
This constructor is provided for those developers who want to use the object initializer pattern. For example:
var command = new PutDataCommand()
{
DataTag = (int)PivDataTag.Authentication;
Data = GetEncodedCertificate();
};
There is no default Tag or EncodedData, hence, for this command to be valid, the Tag and EncodedData must be specified. So if you create an object using this constructor, you must set the Tag and EncodedData properties at some time before using it. Otherwise you will get an exception when you do use it.
PutDataCommand(int, ReadOnlyMemory<byte>)
Initializes a new instance of the PutDataCommand
class.
public PutDataCommand(int dataTag, ReadOnlyMemory<byte> data)
Parameters
dataTag
intThe DataTag indicating where the data will be stored.
data
ReadOnlyMemory<byte>The data to put, encoded as
53 length data
.
Remarks
Note that this constructor requires using a DataTag that is a number
from 0x005F0000
to 0x005FFFFF
inclusive. The data can
be anything but it must be encoded as 53 length data
To encode, you can use the TlvWriter
class.
var tlvWriter = new TlvWriter();
tlvWriter.WriteValue(0x53, dataToStore);
byte[] encoding = tlvWriter.Encode();
Exceptions
- ArgumentException
The DataTag specified is not a number between
0x005F0000
and0x005FFFFF
(inclusive), or0x00007F61
.
Properties
Application
Gets the YubiKeyApplication to which this command belongs. For this command it's PIV.
public YubiKeyApplication Application { get; }
Property Value
- YubiKeyApplication
YubiKeyApplication.Piv
Data
The data that will be put onto the YubiKey.
public ReadOnlyMemory<byte> Data { get; set; }
Property Value
- ReadOnlyMemory<byte>
Exceptions
- ArgumentException
The data supplied is not encoded
53 length data
.
DataTag
The tag specifying where the data will be put.
public int DataTag { get; set; }
Property Value
- int
Exceptions
- ArgumentException
The DataTag specified is not a number between
0x005F0000
and0x005FFFFF
(inclusive), or0x00007F61
.
Methods
CreateCommandApdu()
Creates a well-formed CommandApdu to send to the YubiKey.
public CommandApdu CreateCommandApdu()
Returns
- CommandApdu
A valid CommandApdu that is ready to be sent to the YubiKey, or passed along to additional encoders for further processing.
Remarks
This method will first perform validation on all of the parameters and data provided to it. The CommandAPDU it creates should contain all of the data payload for the command, even if it exceeds 65,535 bytes as specified by the ISO 7816-4 specification. The APDU will be properly chained by the device connection prior to being sent to the YubiKey, and the responses will collapsed into a single result.
CreateResponseForApdu(ResponseApdu)
Creates the corresponding IYubiKeyResponse implementation for the current command.
public PutDataResponse CreateResponseForApdu(ResponseApdu responseApdu)
Parameters
responseApdu
ResponseApduThe ResponseApdu returned by the YubiKey.
Returns
- PutDataResponse
The implementation of IYubiKeyResponse that parses and presents ths response APDU.