Use NIST curve P-384 and the SEC 1 encoding for public keys.

This commit is contained in:
akwizgran
2013-01-03 21:37:42 +00:00
parent f638f1b44e
commit 2dd369214e
4 changed files with 192 additions and 35 deletions

View File

@@ -5,13 +5,21 @@ import static net.sf.briar.api.plugins.InvitationConstants.CODE_BITS;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECField;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
@@ -69,6 +77,41 @@ class CryptoComponentImpl implements CryptoComponent {
private static final byte[] KEY_DERIVATION_BLANK_PLAINTEXT =
new byte[SECRET_KEY_BYTES];
// Parameters for NIST elliptic curve P-384 - see "Suite B Implementer's
// Guide to NIST SP 800-56A", section A.2
private static final BigInteger P_384_Q = new BigInteger("FFFFFFFF" +
"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
"FFFFFFFF", 16);
private static final BigInteger P_384_A = new BigInteger("FFFFFFFF" +
"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
"FFFFFFFC", 16);
private static final BigInteger P_384_B = new BigInteger("B3312FA7" +
"E23EE7E4" + "988E056B" + "E3F82D19" + "181D9C6E" + "FE814112" +
"0314088F" + "5013875A" + "C656398D" + "8A2ED19D" + "2A85C8ED" +
"D3EC2AEF", 16);
private static final BigInteger P_384_G_X = new BigInteger("AA87CA22" +
"BE8B0537" + "8EB1C71E" + "F320AD74" + "6E1D3B62" + "8BA79B98" +
"59F741E0" + "82542A38" + "5502F25D" + "BF55296C" + "3A545E38" +
"72760AB7", 16);
private static final BigInteger P_384_G_Y = new BigInteger("3617DE4A" +
"96262C6F" + "5D9E98BF" + "9292DC29" + "F8F41DBD" + "289A147C" +
"E9DA3113" + "B5F0B8C0" + "0A60B1CE" + "1D7E819D" + "7A431D7C" +
"90EA0E5F", 16);
private static final BigInteger P_384_N = new BigInteger("FFFFFFFF" +
"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
"C7634D81" + "F4372DDF" + "581A0DB2" + "48B0A77A" + "ECEC196A" +
"CCC52973", 16);
private static final int P_384_H = 1;
// Static parameter objects derived from the above parameters
private static final ECField P_384_FIELD = new ECFieldFp(P_384_Q);
private static final EllipticCurve P_384_CURVE =
new EllipticCurve(P_384_FIELD, P_384_A, P_384_B);
private static final ECPoint P_384_G = new ECPoint(P_384_G_X, P_384_G_Y);
private static final ECParameterSpec P_384_PARAMS =
new ECParameterSpec(P_384_CURVE, P_384_G, P_384_N, P_384_H);
private final KeyParser agreementKeyParser, signatureKeyParser;
private final KeyPairGenerator agreementKeyPairGenerator;
private final KeyPairGenerator signatureKeyPairGenerator;
@@ -78,10 +121,14 @@ class CryptoComponentImpl implements CryptoComponent {
CryptoComponentImpl() {
Security.addProvider(new BouncyCastleProvider());
try {
agreementKeyParser = new KeyParserImpl(AGREEMENT_KEY_PAIR_ALGO,
PROVIDER);
signatureKeyParser = new KeyParserImpl(SIGNATURE_KEY_PAIR_ALGO,
PROVIDER);
KeyFactory agreementKeyFactory = KeyFactory.getInstance(
AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
agreementKeyParser = new Sec1KeyParser(agreementKeyFactory,
P_384_PARAMS, P_384_Q, AGREEMENT_KEY_PAIR_BITS);
KeyFactory signatureKeyFactory = KeyFactory.getInstance(
SIGNATURE_KEY_PAIR_ALGO, PROVIDER);
signatureKeyParser = new Sec1KeyParser(signatureKeyFactory,
P_384_PARAMS, P_384_Q, SIGNATURE_KEY_PAIR_BITS);
agreementKeyPairGenerator = KeyPairGenerator.getInstance(
AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
agreementKeyPairGenerator.initialize(AGREEMENT_KEY_PAIR_BITS);
@@ -243,7 +290,29 @@ class CryptoComponentImpl implements CryptoComponent {
}
public KeyPair generateAgreementKeyPair() {
return agreementKeyPairGenerator.generateKeyPair();
KeyPair keyPair = agreementKeyPairGenerator.generateKeyPair();
// Check that the key pair uses NIST curve P-384
ECPublicKey ecPublicKey = checkP384Params(keyPair.getPublic());
// Return a public key that uses the SEC 1 encoding
ecPublicKey = new Sec1PublicKey(ecPublicKey, AGREEMENT_KEY_PAIR_BITS);
return new KeyPair(ecPublicKey, keyPair.getPrivate());
}
private ECPublicKey checkP384Params(PublicKey publicKey) {
if(!(publicKey instanceof ECPublicKey)) throw new RuntimeException();
ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
ECParameterSpec params = ecPublicKey.getParams();
EllipticCurve curve = params.getCurve();
ECField field = curve.getField();
if(!(field instanceof ECFieldFp)) throw new RuntimeException();
BigInteger q = ((ECFieldFp) field).getP();
if(!q.equals(P_384_Q)) throw new RuntimeException();
if(!curve.getA().equals(P_384_A)) throw new RuntimeException();
if(!curve.getB().equals(P_384_B)) throw new RuntimeException();
if(!params.getGenerator().equals(P_384_G)) throw new RuntimeException();
if(!params.getOrder().equals(P_384_N)) throw new RuntimeException();
if(!(params.getCofactor() == P_384_H)) throw new RuntimeException();
return ecPublicKey;
}
public KeyParser getAgreementKeyParser() {
@@ -251,7 +320,12 @@ class CryptoComponentImpl implements CryptoComponent {
}
public KeyPair generateSignatureKeyPair() {
return signatureKeyPairGenerator.generateKeyPair();
KeyPair keyPair = signatureKeyPairGenerator.generateKeyPair();
// Check that the key pair uses NIST curve P-384
ECPublicKey ecPublicKey = checkP384Params(keyPair.getPublic());
// Return a public key that uses the SEC 1 encoding
ecPublicKey = new Sec1PublicKey(ecPublicKey, SIGNATURE_KEY_PAIR_BITS);
return new KeyPair(ecPublicKey, keyPair.getPrivate());
}
public KeyParser getSignatureKeyParser() {
@@ -260,7 +334,7 @@ class CryptoComponentImpl implements CryptoComponent {
public ErasableKey generateTestKey() {
byte[] b = new byte[SECRET_KEY_BYTES];
getSecureRandom().nextBytes(b);
secureRandom.nextBytes(b);
return new ErasableKeyImpl(b, SECRET_KEY_ALGO);
}

View File

@@ -1,28 +0,0 @@
package net.sf.briar.crypto;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import net.sf.briar.api.crypto.KeyParser;
class KeyParserImpl implements KeyParser {
private final KeyFactory keyFactory;
KeyParserImpl(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
keyFactory = KeyFactory.getInstance(algorithm, provider);
}
public PublicKey parsePublicKey(byte[] encodedKey)
throws InvalidKeySpecException {
EncodedKeySpec e = new X509EncodedKeySpec(encodedKey);
return keyFactory.generatePublic(e);
}
}

View File

@@ -0,0 +1,56 @@
package net.sf.briar.crypto;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import net.sf.briar.api.crypto.KeyParser;
/**
* A key parser that uses the encoding defined in "SEC 1: Elliptic Curve
* Cryptography", section 2.3 (Certicom Corporation, May 2009). Point
* compression is not used.
*/
class Sec1KeyParser implements KeyParser {
private final KeyFactory keyFactory;
private final ECParameterSpec params;
private final BigInteger modulus;
private final int bytesPerInt, encodedKeyLength;
Sec1KeyParser(KeyFactory keyFactory, ECParameterSpec params,
BigInteger modulus, int keyBits) {
this.keyFactory = keyFactory;
this.params = params;
this.modulus = modulus;
bytesPerInt = (int) Math.ceil(keyBits / 8.0);
encodedKeyLength = 1 + 2 * bytesPerInt;
}
public PublicKey parsePublicKey(byte[] encodedKey)
throws InvalidKeySpecException {
if(encodedKey.length != encodedKeyLength)
throw new InvalidKeySpecException();
// The first byte must be 0x04
if(encodedKey[0] != 4) throw new InvalidKeySpecException();
// The x co-ordinate must be >= 0 and < q
byte[] xBytes = new byte[bytesPerInt];
System.arraycopy(encodedKey, 1, xBytes, 0, bytesPerInt);
BigInteger x = new BigInteger(1, xBytes); // Positive signum
if(x.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
// The y co-ordinate must be >= 0 and < q
byte[] yBytes = new byte[bytesPerInt];
System.arraycopy(encodedKey, bytesPerInt + 1, yBytes, 0, bytesPerInt);
BigInteger y = new BigInteger(1, yBytes); // Positive signum
if(y.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
// FIXME: Verify that y^2 == x^3 + ax + b (mod q)
// Construct a public key from the point (x, y) and the params
ECPoint pub = new ECPoint(x, y);
ECPublicKeySpec keySpec = new ECPublicKeySpec(pub, params);
return keyFactory.generatePublic(keySpec);
}
}

View File

@@ -0,0 +1,55 @@
package net.sf.briar.crypto;
import java.math.BigInteger;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
/**
* An elliptic curve public key that uses the encoding defined in "SEC 1:
* Elliptic Curve Cryptography", section 2.3 (Certicom Corporation, May 2009).
* Point compression is not used.
*/
class Sec1PublicKey implements ECPublicKey {
private static final long serialVersionUID = -2722797033851423987L;
private final ECPublicKey key;
private final int bytesPerInt, encodedKeyLength;
Sec1PublicKey(ECPublicKey key, int keyBits) {
this.key = key;
bytesPerInt = (int) Math.ceil(keyBits / 8.0);
encodedKeyLength = 1 + 2 * bytesPerInt;
}
public String getAlgorithm() {
return key.getAlgorithm();
}
public byte[] getEncoded() {
byte[] encodedKey = new byte[encodedKeyLength];
encodedKey[0] = 4;
BigInteger x = key.getW().getAffineX(), y = key.getW().getAffineY();
// Copy up to bytesPerInt bytes into exactly bytesPerInt bytes
byte[] xBytes = x.toByteArray();
for(int i = 0; i < xBytes.length && i < bytesPerInt; i++)
encodedKey[bytesPerInt - i] = xBytes[xBytes.length - 1 - i];
byte[] yBytes = y.toByteArray();
for(int i = 0; i < yBytes.length && i < bytesPerInt; i++)
encodedKey[2 * bytesPerInt - i] = yBytes[yBytes.length - 1 - i];
return encodedKey;
}
public String getFormat() {
return "SEC1";
}
public ECParameterSpec getParams() {
return key.getParams();
}
public ECPoint getW() {
return key.getW();
}
}