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