SecurityToken: update put key to support different private key template
This commit is contained in:
@@ -45,6 +45,9 @@ public class OpenPGPCapabilities {
|
||||
public OpenPGPCapabilities(byte[] data) throws IOException {
|
||||
Iso7816TLV[] tlvs = Iso7816TLV.readList(data, true);
|
||||
mKeyFormats = new HashMap<>();
|
||||
if (tlvs.length == 1 && tlvs[0].mT == 0x6E) {
|
||||
tlvs = ((Iso7816TLV.Iso7816CompositeTLV) tlvs[0]).mSubs;
|
||||
}
|
||||
|
||||
for (Iso7816TLV tlv : tlvs) {
|
||||
switch (tlv.mT) {
|
||||
@@ -57,6 +60,21 @@ public class OpenPGPCapabilities {
|
||||
case 0x73:
|
||||
parseDdo((Iso7816TLV.Iso7816CompositeTLV) tlv);
|
||||
break;
|
||||
case 0xC0:
|
||||
parseExtendedCaps(tlv.mV);
|
||||
break;
|
||||
case 0xC1:
|
||||
parseAlgoCaps(KeyType.SIGN, tlv.mV);
|
||||
break;
|
||||
case 0xC2:
|
||||
parseAlgoCaps(KeyType.ENCRYPT, tlv.mV);
|
||||
break;
|
||||
case 0xC3:
|
||||
parseAlgoCaps(KeyType.AUTH, tlv.mV);
|
||||
break;
|
||||
case 0xC4:
|
||||
mPw1ValidForMultipleSignatures = tlv.mV[0] == 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class SecurityTokenHelper {
|
||||
keyType.toString()));
|
||||
}
|
||||
|
||||
putKey(keyType.getSlot(), secretKey, passphrase);
|
||||
putKey(keyType, secretKey, passphrase);
|
||||
putData(keyType.getFingerprintObjectId(), secretKey.getFingerprint());
|
||||
putData(keyType.getTimestampObjectId(), timestampBytes);
|
||||
}
|
||||
@@ -164,7 +164,7 @@ public class SecurityTokenHelper {
|
||||
throw new CardException("Initialization failed!", response.getSW());
|
||||
}
|
||||
|
||||
mOpenPGPCapabilities = new OpenPGPCapabilities(getData(0x00, 0x65));
|
||||
mOpenPGPCapabilities = new OpenPGPCapabilities(getData(0x00, 0x6E));
|
||||
mCardCapabilities = new CardCapabilities(mOpenPGPCapabilities.getHistoricalBytes());
|
||||
|
||||
mPw1ValidatedForSignature = false;
|
||||
@@ -307,12 +307,8 @@ public class SecurityTokenHelper {
|
||||
* 0xB8: Decipherment Key
|
||||
* 0xA4: Authentication Key
|
||||
*/
|
||||
private void putKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
|
||||
private void putKey(KeyType slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
|
||||
throws IOException {
|
||||
if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
|
||||
throw new IOException("Invalid key slot");
|
||||
}
|
||||
|
||||
RSAPrivateCrtKey crtSecretKey;
|
||||
try {
|
||||
secretKey.unlock(passphrase);
|
||||
@@ -335,59 +331,70 @@ public class SecurityTokenHelper {
|
||||
verifyPin(0x83); // (Verify PW3 with mode 83)
|
||||
}
|
||||
|
||||
byte[] header = Hex.decode(
|
||||
"4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23)
|
||||
+ String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length
|
||||
+ "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex)
|
||||
+ "9103" // Public modulus, length 3
|
||||
+ "928180" // Prime P, length 128
|
||||
+ "938180" // Prime Q, length 128
|
||||
+ "948180" // Coefficient (1/q mod p), length 128
|
||||
+ "958180" // Prime exponent P (d mod (p - 1)), length 128
|
||||
+ "968180" // Prime exponent Q (d mod (1 - 1)), length 128
|
||||
+ "97820100" // Modulus, length 256, last item in private key template
|
||||
+ "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow
|
||||
byte[] dataToSend = new byte[934];
|
||||
byte[] currentKeyObject;
|
||||
int offset = 0;
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream(),
|
||||
template = new ByteArrayOutputStream(),
|
||||
data = new ByteArrayOutputStream(),
|
||||
res = new ByteArrayOutputStream();
|
||||
|
||||
System.arraycopy(header, 0, dataToSend, offset, header.length);
|
||||
offset += header.length;
|
||||
currentKeyObject = crtSecretKey.getPublicExponent().toByteArray();
|
||||
System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3);
|
||||
offset += 3;
|
||||
// NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0
|
||||
// in the array to represent sign, so we take care to set the offset to 1 if necessary.
|
||||
currentKeyObject = crtSecretKey.getPrimeP().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
|
||||
Arrays.fill(currentKeyObject, (byte) 0);
|
||||
offset += 128;
|
||||
currentKeyObject = crtSecretKey.getPrimeQ().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
|
||||
Arrays.fill(currentKeyObject, (byte) 0);
|
||||
offset += 128;
|
||||
currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
|
||||
Arrays.fill(currentKeyObject, (byte) 0);
|
||||
offset += 128;
|
||||
currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
|
||||
Arrays.fill(currentKeyObject, (byte) 0);
|
||||
offset += 128;
|
||||
currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
|
||||
Arrays.fill(currentKeyObject, (byte) 0);
|
||||
offset += 128;
|
||||
currentKeyObject = crtSecretKey.getModulus().toByteArray();
|
||||
System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256);
|
||||
// Public modulus, length 3
|
||||
template.write(Hex.decode("9104"));
|
||||
writeBits(data, crtSecretKey.getPublicExponent(), 4);
|
||||
|
||||
// Prime P, length 128
|
||||
template.write(Hex.decode("928180"));
|
||||
writeBits(data, crtSecretKey.getPrimeP(), 128);
|
||||
|
||||
// Prime Q, length 128
|
||||
template.write(Hex.decode("938180"));
|
||||
writeBits(data, crtSecretKey.getPrimeQ(), 128);
|
||||
|
||||
OpenPGPCapabilities.AlgorithmFormat format = mOpenPGPCapabilities.getFormatForKeyType(slot);
|
||||
if (format.isHasExtra()) {
|
||||
// Coefficient (1/q mod p), length 128
|
||||
template.write(Hex.decode("948180"));
|
||||
writeBits(data, crtSecretKey.getCrtCoefficient(), 128);
|
||||
|
||||
// Prime exponent P (d mod (p - 1)), length 128
|
||||
template.write(Hex.decode("958180"));
|
||||
writeBits(data, crtSecretKey.getPrimeExponentP(), 128);
|
||||
|
||||
// Prime exponent Q (d mod (1 - 1)), length 128
|
||||
template.write(Hex.decode("968180"));
|
||||
writeBits(data, crtSecretKey.getPrimeExponentQ(), 128);
|
||||
}
|
||||
|
||||
if (format.isHasModulus()) {
|
||||
// Modulus, length 256, last item in private key template
|
||||
template.write(Hex.decode("97820100"));
|
||||
writeBits(data, crtSecretKey.getModulus(), 256);
|
||||
}
|
||||
|
||||
// Bundle up
|
||||
|
||||
// Ext header list data
|
||||
// Control Reference Template to indicate the private key
|
||||
stream.write(slot.getSlot());
|
||||
stream.write(0);
|
||||
|
||||
// Cardholder private key template
|
||||
stream.write(Hex.decode("7F48"));
|
||||
stream.write(encodeLength(template.size()));
|
||||
stream.write(template.toByteArray());
|
||||
|
||||
// Concatenation of key data as defined in DO 7F48
|
||||
stream.write(Hex.decode("5F48"));
|
||||
stream.write(encodeLength(data.size()));
|
||||
stream.write(data.toByteArray());
|
||||
|
||||
|
||||
// Result tlv
|
||||
res.write(Hex.decode("4D"));
|
||||
res.write(encodeLength(stream.size()));
|
||||
res.write(stream.toByteArray());
|
||||
|
||||
byte[] bytes = res.toByteArray();
|
||||
// Now we're ready to communicate with the token.
|
||||
|
||||
CommandAPDU apdu = new CommandAPDU(0x00, 0xDB, 0x3F, 0xFF, dataToSend);
|
||||
|
||||
// Clear array with secret data before we return.
|
||||
Arrays.fill(dataToSend, (byte) 0);
|
||||
|
||||
CommandAPDU apdu = new CommandAPDU(0x00, 0xDB, 0x3F, 0xFF, bytes);
|
||||
ResponseAPDU response = communicate(apdu);
|
||||
|
||||
if (response.getSW() != APDU_SW_SUCCESS) {
|
||||
@@ -395,6 +402,46 @@ public class SecurityTokenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] encodeLength(int len) {
|
||||
byte[] res;
|
||||
if (len < 128) {
|
||||
res = new byte[1];
|
||||
res[0] = (byte) len;
|
||||
} else if (len < 256) {
|
||||
res = new byte[2];
|
||||
res[0] = -127;
|
||||
res[1] = (byte) len;
|
||||
} else if (len < 65536) {
|
||||
res = new byte[3];
|
||||
res[0] = -126;
|
||||
res[1] = (byte) (len / 256);
|
||||
res[2] = (byte) (len % 256);
|
||||
} else {
|
||||
res = new byte[4];
|
||||
if (len >= 16777216) {
|
||||
throw new IllegalStateException("length [" + len + "] out of range (0x1000000)");
|
||||
}
|
||||
|
||||
res[0] = -125;
|
||||
res[1] = (byte) (len / 65536);
|
||||
res[2] = (byte) (len / 256);
|
||||
res[3] = (byte) (len % 256);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void writeBits(ByteArrayOutputStream stream, BigInteger value, int bits) {
|
||||
byte[] prime = value.toByteArray();
|
||||
byte[] res = new byte[bits];
|
||||
int empty = bits - prime.length + 1;
|
||||
|
||||
System.arraycopy(prime, 1, res, Math.max(0, empty), Math.min(prime.length - 1, bits));
|
||||
|
||||
stream.write(res, 0, bits);
|
||||
Arrays.fill(res, (byte) 0);
|
||||
Arrays.fill(prime, (byte) 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return fingerprints of all keys from application specific data stored
|
||||
* on tag, or null if data not available.
|
||||
|
||||
Reference in New Issue
Block a user