1package org.bouncycastle.jcajce.provider.asymmetric.dh; 2 3import java.math.BigInteger; 4import java.security.InvalidAlgorithmParameterException; 5import java.security.InvalidKeyException; 6import java.security.Key; 7import java.security.NoSuchAlgorithmException; 8import java.security.SecureRandom; 9import java.security.spec.AlgorithmParameterSpec; 10 11import javax.crypto.SecretKey; 12import javax.crypto.ShortBufferException; 13import javax.crypto.interfaces.DHPrivateKey; 14import javax.crypto.interfaces.DHPublicKey; 15import javax.crypto.spec.DHParameterSpec; 16import javax.crypto.spec.SecretKeySpec; 17 18import org.bouncycastle.crypto.DerivationFunction; 19// BEGIN android-removed 20// import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator; 21// import org.bouncycastle.crypto.util.DigestFactory; 22// END android-removed 23import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi; 24import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec; 25 26/** 27 * Diffie-Hellman key agreement. There's actually a better way of doing this 28 * if you are using long term public keys, see the light-weight version for 29 * details. 30 */ 31public class KeyAgreementSpi 32 extends BaseAgreementSpi 33{ 34 private static final BigInteger ONE = BigInteger.valueOf(1); 35 private static final BigInteger TWO = BigInteger.valueOf(2); 36 37 private BigInteger x; 38 private BigInteger p; 39 private BigInteger g; 40 41 private BigInteger result; 42 43 public KeyAgreementSpi() 44 { 45 super("Diffie-Hellman", null); 46 } 47 48 public KeyAgreementSpi( 49 String kaAlgorithm, 50 DerivationFunction kdf) 51 { 52 super(kaAlgorithm, kdf); 53 } 54 55 protected byte[] bigIntToBytes( 56 BigInteger r) 57 { 58 // 59 // RFC 2631 (2.1.2) specifies that the secret should be padded with leading zeros if necessary 60 // must be the same length as p 61 // 62 int expectedLength = (p.bitLength() + 7) / 8; 63 64 byte[] tmp = r.toByteArray(); 65 66 if (tmp.length == expectedLength) 67 { 68 return tmp; 69 } 70 71 if (tmp[0] == 0 && tmp.length == expectedLength + 1) 72 { 73 byte[] rv = new byte[tmp.length - 1]; 74 75 System.arraycopy(tmp, 1, rv, 0, rv.length); 76 return rv; 77 } 78 79 // tmp must be shorter than expectedLength 80 // pad to the left with zeros. 81 byte[] rv = new byte[expectedLength]; 82 83 System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length); 84 85 return rv; 86 } 87 88 protected Key engineDoPhase( 89 Key key, 90 boolean lastPhase) 91 throws InvalidKeyException, IllegalStateException 92 { 93 if (x == null) 94 { 95 throw new IllegalStateException("Diffie-Hellman not initialised."); 96 } 97 98 if (!(key instanceof DHPublicKey)) 99 { 100 throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey"); 101 } 102 DHPublicKey pubKey = (DHPublicKey)key; 103 104 if (!pubKey.getParams().getG().equals(g) || !pubKey.getParams().getP().equals(p)) 105 { 106 throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!"); 107 } 108 109 BigInteger peerY = ((DHPublicKey)key).getY(); 110 if (peerY == null || peerY.compareTo(TWO) < 0 111 || peerY.compareTo(p.subtract(ONE)) >= 0) 112 { 113 throw new InvalidKeyException("Invalid DH PublicKey"); 114 } 115 116 result = peerY.modPow(x, p); 117 if (result.compareTo(ONE) == 0) 118 { 119 throw new InvalidKeyException("Shared key can't be 1"); 120 } 121 122 if (lastPhase) 123 { 124 return null; 125 } 126 127 return new BCDHPublicKey(result, pubKey.getParams()); 128 } 129 130 protected byte[] engineGenerateSecret() 131 throws IllegalStateException 132 { 133 if (x == null) 134 { 135 throw new IllegalStateException("Diffie-Hellman not initialised."); 136 } 137 138 return super.engineGenerateSecret(); 139 } 140 141 protected int engineGenerateSecret( 142 byte[] sharedSecret, 143 int offset) 144 throws IllegalStateException, ShortBufferException 145 { 146 if (x == null) 147 { 148 throw new IllegalStateException("Diffie-Hellman not initialised."); 149 } 150 151 return super.engineGenerateSecret(sharedSecret, offset); 152 } 153 154 protected SecretKey engineGenerateSecret( 155 String algorithm) 156 throws NoSuchAlgorithmException 157 { 158 if (x == null) 159 { 160 throw new IllegalStateException("Diffie-Hellman not initialised."); 161 } 162 163 byte[] res = bigIntToBytes(result); 164 165 // for JSSE compatibility 166 if (algorithm.equals("TlsPremasterSecret")) 167 { 168 return new SecretKeySpec(trimZeroes(res), algorithm); 169 } 170 171 return super.engineGenerateSecret(algorithm); 172 } 173 174 protected void engineInit( 175 Key key, 176 AlgorithmParameterSpec params, 177 SecureRandom random) 178 throws InvalidKeyException, InvalidAlgorithmParameterException 179 { 180 if (!(key instanceof DHPrivateKey)) 181 { 182 throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation"); 183 } 184 DHPrivateKey privKey = (DHPrivateKey)key; 185 186 if (params != null) 187 { 188 if (params instanceof DHParameterSpec) // p, g override. 189 { 190 DHParameterSpec p = (DHParameterSpec)params; 191 192 this.p = p.getP(); 193 this.g = p.getG(); 194 } 195 else if (params instanceof UserKeyingMaterialSpec) 196 { 197 this.p = privKey.getParams().getP(); 198 this.g = privKey.getParams().getG(); 199 this.ukmParameters = ((UserKeyingMaterialSpec)params).getUserKeyingMaterial(); 200 } 201 else 202 { 203 throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec"); 204 } 205 } 206 else 207 { 208 this.p = privKey.getParams().getP(); 209 this.g = privKey.getParams().getG(); 210 } 211 212 this.x = this.result = privKey.getX(); 213 } 214 215 protected void engineInit( 216 Key key, 217 SecureRandom random) 218 throws InvalidKeyException 219 { 220 if (!(key instanceof DHPrivateKey)) 221 { 222 throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey"); 223 } 224 225 DHPrivateKey privKey = (DHPrivateKey)key; 226 227 this.p = privKey.getParams().getP(); 228 this.g = privKey.getParams().getG(); 229 this.x = this.result = privKey.getX(); 230 } 231 232 protected byte[] calcSecret() 233 { 234 return bigIntToBytes(result); 235 } 236 237 // BEGIN android-removed 238 // public static class DHwithRFC2631KDF 239 // extends KeyAgreementSpi 240 // { 241 // public DHwithRFC2631KDF() 242 // { 243 // super("DHwithRFC2631KDF", new DHKEKGenerator(DigestFactory.createSHA1())); 244 // } 245 // } 246 // END android-removed 247} 248