KeyAgreementSpi.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.jcajce.provider.asymmetric.ec; 2 3import java.math.BigInteger; 4import java.security.InvalidAlgorithmParameterException; 5import java.security.InvalidKeyException; 6import java.security.Key; 7import java.security.NoSuchAlgorithmException; 8import java.security.PrivateKey; 9import java.security.PublicKey; 10import java.security.SecureRandom; 11import java.security.spec.AlgorithmParameterSpec; 12import java.util.Hashtable; 13 14import javax.crypto.SecretKey; 15import javax.crypto.ShortBufferException; 16import javax.crypto.spec.SecretKeySpec; 17 18import org.bouncycastle.asn1.DERObjectIdentifier; 19import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 20import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 21import org.bouncycastle.asn1.x9.X9IntegerConverter; 22import org.bouncycastle.crypto.BasicAgreement; 23import org.bouncycastle.crypto.CipherParameters; 24import org.bouncycastle.crypto.DerivationFunction; 25import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; 26// BEGIN android-removed 27// import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement; 28// import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement; 29// import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters; 30// import org.bouncycastle.crypto.agreement.kdf.ECDHKEKGenerator; 31// END android-removed 32import org.bouncycastle.crypto.digests.SHA1Digest; 33import org.bouncycastle.crypto.params.ECDomainParameters; 34import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 35import org.bouncycastle.crypto.params.ECPublicKeyParameters; 36// BEGIN android-removed 37// import org.bouncycastle.crypto.params.MQVPrivateParameters; 38// import org.bouncycastle.crypto.params.MQVPublicParameters; 39// END android-removed 40import org.bouncycastle.jce.interfaces.ECPrivateKey; 41import org.bouncycastle.jce.interfaces.ECPublicKey; 42// BEGIN android-removed 43// import org.bouncycastle.jce.interfaces.MQVPrivateKey; 44// import org.bouncycastle.jce.interfaces.MQVPublicKey; 45// END android-removed 46import org.bouncycastle.util.Integers; 47 48/** 49 * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363 50 * both the simple one, and the simple one with cofactors are supported. 51 * 52 * Also, MQV key agreement per SEC-1 53 */ 54public class KeyAgreementSpi 55 extends javax.crypto.KeyAgreementSpi 56{ 57 private static final X9IntegerConverter converter = new X9IntegerConverter(); 58 private static final Hashtable algorithms = new Hashtable(); 59 60 static 61 { 62 Integer i128 = Integers.valueOf(128); 63 Integer i192 = Integers.valueOf(192); 64 Integer i256 = Integers.valueOf(256); 65 66 algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); 67 algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); 68 algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); 69 algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); 70 algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); 71 algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); 72 algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); 73 } 74 75 private String kaAlgorithm; 76 private BigInteger result; 77 private ECDomainParameters parameters; 78 private BasicAgreement agreement; 79 // BEGIN android-removed 80 // private DerivationFunction kdf; 81 // END android-removed 82 83 private byte[] bigIntToBytes( 84 BigInteger r) 85 { 86 return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getX())); 87 } 88 89 protected KeyAgreementSpi( 90 String kaAlgorithm, 91 BasicAgreement agreement, 92 DerivationFunction kdf) 93 { 94 this.kaAlgorithm = kaAlgorithm; 95 this.agreement = agreement; 96 // BEGIN android-removed 97 // this.kdf = kdf; 98 // END android-removed 99 } 100 101 protected Key engineDoPhase( 102 Key key, 103 boolean lastPhase) 104 throws InvalidKeyException, IllegalStateException 105 { 106 if (parameters == null) 107 { 108 throw new IllegalStateException(kaAlgorithm + " not initialised."); 109 } 110 111 if (!lastPhase) 112 { 113 throw new IllegalStateException(kaAlgorithm + " can only be between two parties."); 114 } 115 116 CipherParameters pubKey; 117 // BEGIN android-removed 118 // if (agreement instanceof ECMQVBasicAgreement) 119 // { 120 // if (!(key instanceof MQVPublicKey)) 121 // { 122 // throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 123 // + getSimpleName(MQVPublicKey.class) + " for doPhase"); 124 // } 125 // 126 // MQVPublicKey mqvPubKey = (MQVPublicKey)key; 127 // ECPublicKeyParameters staticKey = (ECPublicKeyParameters) 128 // ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey()); 129 // ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) 130 // ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey()); 131 // 132 // pubKey = new MQVPublicParameters(staticKey, ephemKey); 133 // 134 // // TODO Validate that all the keys are using the same parameters? 135 // } 136 // else 137 // END android-removed 138 { 139 if (!(key instanceof PublicKey)) 140 { 141 throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 142 + getSimpleName(ECPublicKey.class) + " for doPhase"); 143 } 144 145 pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key); 146 147 // TODO Validate that all the keys are using the same parameters? 148 } 149 150 result = agreement.calculateAgreement(pubKey); 151 152 return null; 153 } 154 155 protected byte[] engineGenerateSecret() 156 throws IllegalStateException 157 { 158 // BEGIN android-removed 159 // if (kdf != null) 160 // { 161 // throw new UnsupportedOperationException( 162 // "KDF can only be used when algorithm is known"); 163 // } 164 // END android-removed 165 166 return bigIntToBytes(result); 167 } 168 169 protected int engineGenerateSecret( 170 byte[] sharedSecret, 171 int offset) 172 throws IllegalStateException, ShortBufferException 173 { 174 byte[] secret = engineGenerateSecret(); 175 176 if (sharedSecret.length - offset < secret.length) 177 { 178 throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); 179 } 180 181 System.arraycopy(secret, 0, sharedSecret, offset, secret.length); 182 183 return secret.length; 184 } 185 186 protected SecretKey engineGenerateSecret( 187 String algorithm) 188 throws NoSuchAlgorithmException 189 { 190 byte[] secret = bigIntToBytes(result); 191 192 // BEGIN android-removed 193 // if (kdf != null) 194 // { 195 // if (!algorithms.containsKey(algorithm)) 196 // { 197 // throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm); 198 // } 199 // 200 // int keySize = ((Integer)algorithms.get(algorithm)).intValue(); 201 // 202 // DHKDFParameters params = new DHKDFParameters(new DERObjectIdentifier(algorithm), keySize, secret); 203 // 204 // byte[] keyBytes = new byte[keySize / 8]; 205 // kdf.init(params); 206 // kdf.generateBytes(keyBytes, 0, keyBytes.length); 207 // secret = keyBytes; 208 // } 209 // else 210 // END android-removed 211 { 212 // TODO Should we be ensuring the key is the right length? 213 } 214 215 return new SecretKeySpec(secret, algorithm); 216 } 217 218 protected void engineInit( 219 Key key, 220 AlgorithmParameterSpec params, 221 SecureRandom random) 222 throws InvalidKeyException, InvalidAlgorithmParameterException 223 { 224 initFromKey(key); 225 } 226 227 protected void engineInit( 228 Key key, 229 SecureRandom random) 230 throws InvalidKeyException 231 { 232 initFromKey(key); 233 } 234 235 private void initFromKey(Key key) 236 throws InvalidKeyException 237 { 238 // BEGIN android-removed 239 // if (agreement instanceof ECMQVBasicAgreement) 240 // { 241 // if (!(key instanceof MQVPrivateKey)) 242 // { 243 // throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 244 // + getSimpleName(MQVPrivateKey.class) + " for initialisation"); 245 // } 246 // 247 // MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key; 248 // ECPrivateKeyParameters staticPrivKey = (ECPrivateKeyParameters) 249 // ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey()); 250 // ECPrivateKeyParameters ephemPrivKey = (ECPrivateKeyParameters) 251 // ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey()); 252 // 253 // ECPublicKeyParameters ephemPubKey = null; 254 // if (mqvPrivKey.getEphemeralPublicKey() != null) 255 // { 256 // ephemPubKey = (ECPublicKeyParameters) 257 // ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey()); 258 // } 259 // 260 // MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey); 261 // this.parameters = staticPrivKey.getParameters(); 262 // 263 // // TODO Validate that all the keys are using the same parameters? 264 // 265 // agreement.init(localParams); 266 // } 267 // else 268 // END android-removed 269 { 270 if (!(key instanceof PrivateKey)) 271 { 272 throw new InvalidKeyException(kaAlgorithm + " key agreement requires " 273 + getSimpleName(ECPrivateKey.class) + " for initialisation"); 274 } 275 276 ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key); 277 this.parameters = privKey.getParameters(); 278 279 agreement.init(privKey); 280 } 281 } 282 283 private static String getSimpleName(Class clazz) 284 { 285 String fullName = clazz.getName(); 286 287 return fullName.substring(fullName.lastIndexOf('.') + 1); 288 } 289 290 public static class DH 291 extends KeyAgreementSpi 292 { 293 public DH() 294 { 295 super("ECDH", new ECDHBasicAgreement(), null); 296 } 297 } 298 299 // BEGIN android-removed 300 // public static class DHC 301 // extends KeyAgreementSpi 302 // { 303 // public DHC() 304 // { 305 // super("ECDHC", new ECDHCBasicAgreement(), null); 306 // } 307 // } 308 // 309 // public static class MQV 310 // extends KeyAgreementSpi 311 // { 312 // public MQV() 313 // { 314 // super("ECMQV", new ECMQVBasicAgreement(), null); 315 // } 316 // } 317 // 318 // public static class DHwithSHA1KDF 319 // extends KeyAgreementSpi 320 // { 321 // public DHwithSHA1KDF() 322 // { 323 // super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); 324 // } 325 // } 326 // 327 // public static class MQVwithSHA1KDF 328 // extends KeyAgreementSpi 329 // { 330 // public MQVwithSHA1KDF() 331 // { 332 // super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); 333 // } 334 // } 335 // END android-removed 336} 337