Support of OpenPGP card v3
This commit is contained in:
@@ -18,14 +18,27 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
|
||||
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
|
||||
import org.bouncycastle.bcpg.HashAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.sig.KeyFlags;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||
import org.bouncycastle.openpgp.operator.RFC6637Utils;
|
||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
|
||||
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
@@ -189,4 +202,62 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
return !isRevoked() && !isExpired();
|
||||
}
|
||||
|
||||
// For use only in card export; returns the public key.
|
||||
public ECPublicKey getECPublicKey()
|
||||
throws PgpGeneralException {
|
||||
JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
|
||||
PublicKey retVal;
|
||||
try {
|
||||
retVal = keyConverter.getPublicKey(mPublicKey);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error converting public key!", e);
|
||||
}
|
||||
|
||||
return (ECPublicKey) retVal;
|
||||
}
|
||||
|
||||
public ASN1ObjectIdentifier getHashAlgorithm()
|
||||
throws PGPException {
|
||||
if (!isEC()) {
|
||||
throw new PGPException("Key encryption OID is valid only for EC key!");
|
||||
}
|
||||
|
||||
final ECDHPublicBCPGKey eck = (ECDHPublicBCPGKey)mPublicKey.getPublicKeyPacket().getKey();
|
||||
|
||||
switch (eck.getHashAlgorithm()) {
|
||||
case HashAlgorithmTags.SHA256:
|
||||
return NISTObjectIdentifiers.id_sha256;
|
||||
case HashAlgorithmTags.SHA384:
|
||||
return NISTObjectIdentifiers.id_sha384;
|
||||
case HashAlgorithmTags.SHA512:
|
||||
return NISTObjectIdentifiers.id_sha512;
|
||||
default:
|
||||
throw new PGPException("Invalid hash algorithm for EC key : " + eck.getHashAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
public int getSymmetricKeySize()
|
||||
throws PGPException {
|
||||
if (!isEC()) {
|
||||
throw new PGPException("Key encryption OID is valid only for EC key!");
|
||||
}
|
||||
|
||||
final ECDHPublicBCPGKey eck = (ECDHPublicBCPGKey)mPublicKey.getPublicKeyPacket().getKey();
|
||||
|
||||
switch (eck.getSymmetricKeyAlgorithm()) {
|
||||
case SymmetricKeyAlgorithmTags.AES_128:
|
||||
return 128;
|
||||
case SymmetricKeyAlgorithmTags.AES_192:
|
||||
return 192;
|
||||
case SymmetricKeyAlgorithmTags.AES_256:
|
||||
return 256;
|
||||
default:
|
||||
throw new PGPException("Invalid symmetric encryption algorithm for EC key : " + eck.getSymmetricKeyAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] createUserKeyingMaterial(KeyFingerPrintCalculator fingerPrintCalculator)
|
||||
throws IOException, PGPException {
|
||||
return RFC6637Utils.createUserKeyingMaterial(mPublicKey.getPublicKeyPacket(), fingerPrintCalculator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -319,6 +320,28 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
return (RSAPrivateCrtKey)retVal;
|
||||
}
|
||||
|
||||
// For use only in card export; returns the secret key.
|
||||
public ECPrivateKey getECSecretKey()
|
||||
throws PgpGeneralException {
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||
throw new PgpGeneralException("Cannot get secret key attributes while key is locked.");
|
||||
}
|
||||
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||
throw new PgpGeneralException("Cannot get secret key attributes of divert-to-card key.");
|
||||
}
|
||||
|
||||
JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
|
||||
PrivateKey retVal;
|
||||
try {
|
||||
retVal = keyConverter.getPrivateKey(mPrivateKey);
|
||||
} catch (PGPException e) {
|
||||
throw new PgpGeneralException("Error converting private key! " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return (ECPrivateKey) retVal;
|
||||
}
|
||||
|
||||
public byte[] getIv() {
|
||||
return mSecretKey.getIV();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||
import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
|
||||
import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
|
||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||
import org.bouncycastle.bcpg.S2K;
|
||||
import org.bouncycastle.bcpg.sig.Features;
|
||||
@@ -1645,20 +1649,44 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
private static boolean checkSecurityTokenCompatibility(PGPSecretKey key, OperationLog log, int indent) {
|
||||
PGPPublicKey publicKey = key.getPublicKey();
|
||||
int algorithm = publicKey.getAlgorithm();
|
||||
if (algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT &&
|
||||
algorithm != PublicKeyAlgorithmTags.RSA_SIGN &&
|
||||
algorithm != PublicKeyAlgorithmTags.RSA_GENERAL) {
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO, indent + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Key size must be 2048
|
||||
int keySize = publicKey.getBitStrength();
|
||||
if (keySize != 2048) {
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE, indent + 1);
|
||||
return false;
|
||||
final PGPPublicKey publicKey = key.getPublicKey();
|
||||
ASN1ObjectIdentifier curve;
|
||||
|
||||
switch (publicKey.getAlgorithm()) {
|
||||
case PublicKeyAlgorithmTags.RSA_ENCRYPT:
|
||||
case PublicKeyAlgorithmTags.RSA_SIGN:
|
||||
case PublicKeyAlgorithmTags.RSA_GENERAL:
|
||||
// Key size must be at least 2048
|
||||
if (publicKey.getBitStrength() < 2048) {
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE, indent + 1);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PublicKeyAlgorithmTags.ECDH:
|
||||
curve = ((ECDHPublicBCPGKey)(publicKey.getPublicKeyPacket().getKey())).getCurveOID();
|
||||
if (!curve.equals(NISTNamedCurves.getOID("P-256")) &&
|
||||
!curve.equals(NISTNamedCurves.getOID("P-384")) &&
|
||||
!curve.equals(NISTNamedCurves.getOID("P-521"))) {
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_CURVE, indent + 1);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PublicKeyAlgorithmTags.ECDSA:
|
||||
curve = ((ECDSAPublicBCPGKey)(publicKey.getPublicKeyPacket().getKey())).getCurveOID();
|
||||
if (!curve.equals(NISTNamedCurves.getOID("P-256")) &&
|
||||
!curve.equals(NISTNamedCurves.getOID("P-384")) &&
|
||||
!curve.equals(NISTNamedCurves.getOID("P-521"))) {
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_CURVE, indent + 1);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO, indent + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Secret key parts must be available
|
||||
|
||||
Reference in New Issue
Block a user