Retain RSA key format when setting key attributes in putKey operation
For the put secret key operation, openpgp applet implementations differ in their handling of attributes: - there are four formats for sending key data: standard, standard with modulus, with crt, and with crt and modulus. - the key attributes (modulus length, public exponent length, key format) can not be changed on all cards. changing them is only necessary for cards that support different key lengths (that is, RSA 4096) - on the cards where they *can* be changed, not all parameters might be changeable. in particular, modulus length may be changeable but not key format. Because of this constellation, the put key operation now only sets the modulus of the key, while retaining the key format. At the time of writing, the Gnuk and Nitrokey use the standard format, while the Yubikey and other applets use crt+modulus. This fixes loading keys into the Nitrokey Pro, and partially for the Gnuk token.
This commit is contained in:
parent
5f622339b1
commit
778fb8e94a
|
@ -20,20 +20,22 @@ package org.sufficientlysecure.keychain.securitytoken;
|
|||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
||||
|
||||
public enum KeyType {
|
||||
SIGN(0, 0xB6, 0xCE, 0xC7),
|
||||
ENCRYPT(1, 0xB8, 0xCF, 0xC8),
|
||||
AUTH(2, 0xA4, 0xD0, 0xC9),;
|
||||
SIGN(0, 0xB6, 0xCE, 0xC7, 0xC1),
|
||||
ENCRYPT(1, 0xB8, 0xCF, 0xC8, 0xC2),
|
||||
AUTH(2, 0xA4, 0xD0, 0xC9, 0xC3);
|
||||
|
||||
private final int mIdx;
|
||||
private final int mSlot;
|
||||
private final int mTimestampObjectId;
|
||||
private final int mFingerprintObjectId;
|
||||
private final int mAlgoAttributeSlot;
|
||||
|
||||
KeyType(final int idx, final int slot, final int timestampObjectId, final int fingerprintObjectId) {
|
||||
KeyType(int idx, int slot, int timestampObjectId, int fingerprintObjectId, int algoAttributeSlot) {
|
||||
this.mIdx = idx;
|
||||
this.mSlot = slot;
|
||||
this.mTimestampObjectId = timestampObjectId;
|
||||
this.mFingerprintObjectId = fingerprintObjectId;
|
||||
this.mAlgoAttributeSlot = algoAttributeSlot;
|
||||
}
|
||||
|
||||
public static KeyType from(final CanonicalizedSecretKey key) {
|
||||
|
@ -62,4 +64,8 @@ public enum KeyType {
|
|||
public int getFingerprintObjectId() {
|
||||
return mFingerprintObjectId;
|
||||
}
|
||||
|
||||
public int getAlgoAttributeSlot() {
|
||||
return mAlgoAttributeSlot;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,8 +193,7 @@ public class SecurityTokenConnection {
|
|||
throw new CardException("Initialization failed!", response.getSw());
|
||||
}
|
||||
|
||||
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(getData(0x00, 0x6E));
|
||||
setConnectionCapabilities(openPgpCapabilities);
|
||||
refreshConnectionCapabilities();
|
||||
|
||||
mPw1ValidatedForSignature = false;
|
||||
mPw1ValidatedForDecrypt = false;
|
||||
|
@ -235,6 +234,13 @@ public class SecurityTokenConnection {
|
|||
tokenType = TokenType.UNKNOWN;
|
||||
}
|
||||
|
||||
private void refreshConnectionCapabilities() throws IOException {
|
||||
byte[] rawOpenPgpCapabilities = getData(0x00, 0x6E);
|
||||
|
||||
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(rawOpenPgpCapabilities);
|
||||
setConnectionCapabilities(openPgpCapabilities);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException {
|
||||
this.mOpenPgpCapabilities = openPgpCapabilities;
|
||||
|
@ -493,33 +499,13 @@ public class SecurityTokenConnection {
|
|||
}
|
||||
}
|
||||
|
||||
private void setKeyAttributes(Passphrase adminPin, final KeyType slot, final CanonicalizedSecretKey secretKey)
|
||||
throws IOException {
|
||||
|
||||
if (mOpenPgpCapabilities.isAttributesChangable()) {
|
||||
int tag;
|
||||
|
||||
if (slot == KeyType.SIGN) {
|
||||
tag = 0xC1;
|
||||
} else if (slot == KeyType.ENCRYPT) {
|
||||
tag = 0xC2;
|
||||
} else if (slot == KeyType.AUTH) {
|
||||
tag = 0xC3;
|
||||
} else {
|
||||
throw new IOException("Unknown key for card.");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
putData(adminPin, tag, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey));
|
||||
|
||||
mOpenPgpCapabilities.updateWithData(getData(0x00, tag));
|
||||
|
||||
} catch (PgpGeneralException e) {
|
||||
throw new IOException("Key algorithm not supported by the security token.");
|
||||
}
|
||||
|
||||
private void setKeyAttributes(Passphrase adminPin, KeyType keyType, byte[] data) throws IOException {
|
||||
if (!mOpenPgpCapabilities.isAttributesChangable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
putData(adminPin, keyType.getAlgoAttributeSlot(), data);
|
||||
refreshConnectionCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -546,9 +532,11 @@ public class SecurityTokenConnection {
|
|||
try {
|
||||
secretKey.unlock(passphrase);
|
||||
|
||||
setKeyAttributes(adminPin, slot, secretKey);
|
||||
setKeyAttributes(adminPin, slot, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey,
|
||||
mOpenPgpCapabilities.getFormatForKeyType(slot)));
|
||||
|
||||
switch (mOpenPgpCapabilities.getFormatForKeyType(slot).keyFormatType()) {
|
||||
KeyFormat formatForKeyType = mOpenPgpCapabilities.getFormatForKeyType(slot);
|
||||
switch (formatForKeyType.keyFormatType()) {
|
||||
case RSAKeyFormatType:
|
||||
if (!secretKey.isRSA()) {
|
||||
throw new IOException("Security Token not configured for RSA key.");
|
||||
|
@ -561,7 +549,7 @@ public class SecurityTokenConnection {
|
|||
}
|
||||
|
||||
keyBytes = SecurityTokenUtils.createRSAPrivKeyTemplate(crtSecretKey, slot,
|
||||
(RSAKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
|
||||
(RSAKeyFormat) formatForKeyType);
|
||||
break;
|
||||
|
||||
case ECKeyFormatType:
|
||||
|
@ -574,7 +562,7 @@ public class SecurityTokenConnection {
|
|||
ecPublicKey = secretKey.getSecurityTokenECPublicKey();
|
||||
|
||||
keyBytes = SecurityTokenUtils.createECPrivKeyTemplate(ecSecretKey, ecPublicKey, slot,
|
||||
(ECKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
|
||||
(ECKeyFormat) formatForKeyType);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -22,9 +22,6 @@ import org.bouncycastle.util.Arrays;
|
|||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.securitytoken.ECKeyFormat;
|
||||
import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
|
||||
import org.sufficientlysecure.keychain.securitytoken.KeyType;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -33,8 +30,10 @@ import java.security.interfaces.ECPrivateKey;
|
|||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
|
||||
|
||||
class SecurityTokenUtils {
|
||||
static byte[] attributesFromSecretKey(final KeyType slot, final CanonicalizedSecretKey secretKey) throws IOException, PgpGeneralException {
|
||||
static byte[] attributesFromSecretKey(KeyType slot, CanonicalizedSecretKey secretKey, KeyFormat formatForKeyType)
|
||||
throws IOException, PgpGeneralException {
|
||||
if (secretKey.isRSA()) {
|
||||
final int mModulusLength = secretKey.getBitStrength();
|
||||
final int mExponentLength = secretKey.getSecurityTokenRSASecretKey().getPublicExponent().bitLength();
|
||||
|
@ -46,7 +45,7 @@ class SecurityTokenUtils {
|
|||
attrs[i++] = (byte) (mModulusLength & 0xff);
|
||||
attrs[i++] = (byte) ((mExponentLength >> 8) & 0xff);
|
||||
attrs[i++] = (byte) (mExponentLength & 0xff);
|
||||
attrs[i] = RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS.getValue();
|
||||
attrs[i] = ((RSAKeyFormat) formatForKeyType).getAlgorithmFormat().getValue();
|
||||
|
||||
return attrs;
|
||||
} else if (secretKey.isEC()) {
|
||||
|
|
Loading…
Reference in a new issue