1 /* DiffieHellmanImpl.java -- implementation of the Diffie-Hellman key agreement.
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu
.javax
.crypto
;
41 import gnu
.classpath
.ByteArray
;
42 import gnu
.classpath
.debug
.Component
;
43 import gnu
.classpath
.debug
.SystemLogger
;
45 import java
.math
.BigInteger
;
47 import java
.security
.AlgorithmParameters
;
48 import java
.security
.InvalidAlgorithmParameterException
;
49 import java
.security
.InvalidKeyException
;
50 import java
.security
.Key
;
51 import java
.security
.NoSuchAlgorithmException
;
52 import java
.security
.SecureRandom
;
54 import java
.security
.interfaces
.RSAKey
;
55 import java
.security
.interfaces
.RSAPrivateKey
;
56 import java
.security
.interfaces
.RSAPrivateCrtKey
;
57 import java
.security
.interfaces
.RSAPublicKey
;
59 import java
.security
.spec
.AlgorithmParameterSpec
;
61 import java
.util
.logging
.Logger
;
63 import javax
.crypto
.BadPaddingException
;
64 import javax
.crypto
.Cipher
;
65 import javax
.crypto
.CipherSpi
;
66 import javax
.crypto
.IllegalBlockSizeException
;
67 import javax
.crypto
.NoSuchPaddingException
;
68 import javax
.crypto
.ShortBufferException
;
70 public class RSACipherImpl
extends CipherSpi
72 private static final Logger logger
= SystemLogger
.SYSTEM
;
74 private static final byte[] EMPTY
= new byte[0];
75 private int opmode
= -1;
76 private RSAPrivateKey decipherKey
= null;
77 private RSAPublicKey blindingKey
= null;
78 private RSAPublicKey encipherKey
= null;
79 private SecureRandom random
= null;
80 private byte[] dataBuffer
= null;
83 protected void engineSetMode (String mode
) throws NoSuchAlgorithmException
85 throw new NoSuchAlgorithmException ("only one mode available");
88 protected void engineSetPadding (String pad
) throws NoSuchPaddingException
90 throw new NoSuchPaddingException ("only one padding available");
93 protected int engineGetBlockSize ()
98 protected int engineGetOutputSize (int inputLen
)
101 if (decipherKey
!= null)
103 outputLen
= (decipherKey
.getModulus ().bitLength () + 7) / 8;
105 else if (encipherKey
!= null)
107 outputLen
= (encipherKey
.getModulus ().bitLength () + 7) / 8;
110 throw new IllegalStateException ("not initialized");
111 if (inputLen
> outputLen
)
112 throw new IllegalArgumentException ("not configured to encode " + inputLen
113 + "bytes; at most " + outputLen
);
117 protected int engineGetKeySize (final Key key
) throws InvalidKeyException
119 if (!(key
instanceof RSAKey
))
120 throw new InvalidKeyException ("not an RSA key");
121 return ((RSAKey
) key
).getModulus ().bitLength ();
124 protected byte[] engineGetIV ()
129 protected AlgorithmParameters
engineGetParameters()
134 protected void engineInit (int opmode
, Key key
, SecureRandom random
)
135 throws InvalidKeyException
138 if (opmode
== Cipher
.ENCRYPT_MODE
)
140 if (!(key
instanceof RSAPublicKey
))
141 throw new InvalidKeyException ("expecting a RSAPublicKey");
142 encipherKey
= (RSAPublicKey
) key
;
145 outputLen
= (encipherKey
.getModulus ().bitLength () + 7) / 8;
147 else if (opmode
== Cipher
.DECRYPT_MODE
)
149 if (key
instanceof RSAPrivateKey
)
151 decipherKey
= (RSAPrivateKey
) key
;
154 outputLen
= (decipherKey
.getModulus ().bitLength () + 7) / 8;
156 else if (key
instanceof RSAPublicKey
)
158 if (decipherKey
== null)
159 throw new IllegalStateException ("must configure decryption key first");
160 if (!decipherKey
.getModulus ().equals (((RSAPublicKey
) key
).getModulus ()))
161 throw new InvalidKeyException ("blinding key is not compatible");
162 blindingKey
= (RSAPublicKey
) key
;
166 throw new InvalidKeyException ("expecting either an RSAPrivateKey or an RSAPublicKey (for blinding)");
169 throw new IllegalArgumentException ("only encryption and decryption supported");
170 this.random
= random
;
171 this.opmode
= opmode
;
173 dataBuffer
= new byte[outputLen
];
176 protected void engineInit (int opmode
, Key key
, AlgorithmParameterSpec spec
, SecureRandom random
)
177 throws InvalidKeyException
179 engineInit (opmode
, key
, random
);
182 protected void engineInit (int opmode
, Key key
, AlgorithmParameters params
, SecureRandom random
)
183 throws InvalidKeyException
185 engineInit (opmode
, key
, random
);
188 protected byte[] engineUpdate (byte[] in
, int offset
, int length
)
190 if (opmode
!= Cipher
.ENCRYPT_MODE
&& opmode
!= Cipher
.DECRYPT_MODE
)
191 throw new IllegalStateException ("not initialized");
192 System
.arraycopy (in
, offset
, dataBuffer
, pos
, length
);
197 protected int engineUpdate (byte[] in
, int offset
, int length
, byte[] out
, int outOffset
)
199 engineUpdate (in
, offset
, length
);
203 protected byte[] engineDoFinal (byte[] in
, int offset
, int length
)
204 throws IllegalBlockSizeException
, BadPaddingException
206 engineUpdate (in
, offset
, length
);
207 if (opmode
== Cipher
.DECRYPT_MODE
)
209 if (pos
< dataBuffer
.length
)
210 throw new IllegalBlockSizeException ("expecting exactly " + dataBuffer
.length
+ " bytes");
211 BigInteger enc
= new BigInteger (1, dataBuffer
);
212 byte[] dec
= rsaDecrypt (enc
);
213 logger
.log (Component
.CRYPTO
, "RSA: decryption produced\n{0}",
214 new ByteArray (dec
));
216 throw new BadPaddingException ("expected padding type 2");
218 for (i
= 1; i
< dec
.length
&& dec
[i
] != 0x00; i
++);
219 int len
= dec
.length
- i
;
220 byte[] result
= new byte[len
];
221 System
.arraycopy (dec
, i
, result
, 0, len
);
227 offset
= dataBuffer
.length
- pos
;
229 throw new IllegalBlockSizeException ("input is too large to encrypt");
230 byte[] dec
= new byte[dataBuffer
.length
];
233 random
= new SecureRandom ();
234 byte[] pad
= new byte[offset
- 2];
235 random
.nextBytes (pad
);
236 for (int i
= 0; i
< pad
.length
; i
++)
239 System
.arraycopy (pad
, 0, dec
, 1, pad
.length
);
240 dec
[dec
.length
- pos
] = 0x00;
241 System
.arraycopy (dataBuffer
, 0, dec
, offset
, pos
);
242 logger
.log (Component
.CRYPTO
, "RSA: produced padded plaintext\n{0}",
243 new ByteArray (dec
));
244 BigInteger x
= new BigInteger (1, dec
);
245 BigInteger y
= x
.modPow (encipherKey
.getPublicExponent (),
246 encipherKey
.getModulus ());
247 byte[] enc
= y
.toByteArray ();
250 byte[] tmp
= new byte[enc
.length
- 1];
251 System
.arraycopy (enc
, 1, tmp
, 0, tmp
.length
);
259 protected int engineDoFinal (byte[] out
, int offset
)
260 throws ShortBufferException
, IllegalBlockSizeException
, BadPaddingException
262 byte[] result
= engineDoFinal (EMPTY
, 0, 0);
263 if (out
.length
- offset
< result
.length
)
264 throw new ShortBufferException ("need " + result
.length
+ ", have "
265 + (out
.length
- offset
));
266 System
.arraycopy (result
, 0, out
, offset
, result
.length
);
267 return result
.length
;
270 protected int engineDoFinal (final byte[] input
, final int offset
, final int length
,
271 final byte[] output
, final int outputOffset
)
272 throws ShortBufferException
, IllegalBlockSizeException
, BadPaddingException
274 byte[] result
= engineDoFinal (input
, offset
, length
);
275 if (output
.length
- outputOffset
< result
.length
)
276 throw new ShortBufferException ("need " + result
.length
+ ", have "
277 + (output
.length
- outputOffset
));
278 System
.arraycopy (result
, 0, output
, outputOffset
, result
.length
);
279 return result
.length
;
283 * Decrypts the ciphertext, employing RSA blinding if possible.
285 private byte[] rsaDecrypt (BigInteger enc
)
288 random
= new SecureRandom ();
289 BigInteger n
= decipherKey
.getModulus ();
291 BigInteger pubExp
= null;
292 if (blindingKey
!= null)
293 pubExp
= blindingKey
.getPublicExponent ();
294 if (pubExp
!= null && (decipherKey
instanceof RSAPrivateCrtKey
))
295 pubExp
= ((RSAPrivateCrtKey
) decipherKey
).getPublicExponent ();
298 r
= new BigInteger (n
.bitLength () - 1, random
);
299 enc
= r
.modPow (pubExp
, n
).multiply (enc
).mod (n
);
302 BigInteger dec
= enc
.modPow (decipherKey
.getPrivateExponent (), n
);
306 dec
= dec
.multiply (r
.modInverse (n
)).mod (n
);
309 return dec
.toByteArray ();