NativeRSACipher.java revision 10854:558e97e47abe
1251607Sdim/*
2251607Sdim * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3251607Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4251607Sdim *
5251607Sdim * This code is free software; you can redistribute it and/or modify it
6251607Sdim * under the terms of the GNU General Public License version 2 only, as
7251607Sdim * published by the Free Software Foundation.  Oracle designates this
8251607Sdim * particular file as subject to the "Classpath" exception as provided
9251607Sdim * by Oracle in the LICENSE file that accompanied this code.
10251607Sdim *
11251607Sdim * This code is distributed in the hope that it will be useful, but WITHOUT
12251607Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13251607Sdim * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14251607Sdim * version 2 for more details (a copy is included in the LICENSE file that
15251607Sdim * accompanied this code).
16251607Sdim *
17251607Sdim * You should have received a copy of the GNU General Public License version
18251607Sdim * 2 along with this work; if not, write to the Free Software Foundation,
19251607Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20251607Sdim *
21251607Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22251607Sdim * or visit www.oracle.com if you need additional information or have any
23251607Sdim * questions.
24251607Sdim */
25251607Sdim
26251607Sdimpackage com.oracle.security.ucrypto;
27251607Sdim
28251607Sdimimport java.util.Arrays;
29251607Sdimimport java.util.WeakHashMap;
30251607Sdimimport java.util.Collections;
31251607Sdimimport java.util.Map;
32251607Sdim
33251607Sdimimport java.security.AlgorithmParameters;
34251607Sdimimport java.security.InvalidAlgorithmParameterException;
35251607Sdimimport java.security.InvalidKeyException;
36251607Sdimimport java.security.Key;
37251607Sdimimport java.security.PublicKey;
38251607Sdimimport java.security.PrivateKey;
39251607Sdimimport java.security.spec.RSAPrivateCrtKeySpec;
40251607Sdimimport java.security.spec.RSAPublicKeySpec;
41251607Sdimimport java.security.interfaces.RSAKey;
42251607Sdimimport java.security.interfaces.RSAPrivateCrtKey;
43251607Sdimimport java.security.interfaces.RSAPublicKey;
44251607Sdim
45251607Sdimimport java.security.KeyFactory;
46251607Sdimimport java.security.NoSuchAlgorithmException;
47import java.security.SecureRandom;
48
49import java.security.spec.AlgorithmParameterSpec;
50import java.security.spec.InvalidParameterSpecException;
51import java.security.spec.InvalidKeySpecException;
52
53import javax.crypto.BadPaddingException;
54import javax.crypto.Cipher;
55import javax.crypto.CipherSpi;
56import javax.crypto.SecretKey;
57import javax.crypto.IllegalBlockSizeException;
58import javax.crypto.NoSuchPaddingException;
59import javax.crypto.ShortBufferException;
60
61import javax.crypto.spec.SecretKeySpec;
62
63import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
64import sun.security.util.KeyUtil;
65
66/**
67 * Asymmetric Cipher wrapper class utilizing ucrypto APIs. This class
68 * currently supports
69 * - RSA/ECB/NOPADDING
70 * - RSA/ECB/PKCS1PADDING
71 *
72 * @since 1.9
73 */
74public class NativeRSACipher extends CipherSpi {
75    // fields set in constructor
76    private final UcryptoMech mech;
77    private final int padLen;
78    private final NativeRSAKeyFactory keyFactory;
79    private AlgorithmParameterSpec spec;
80    private SecureRandom random;
81
82    // Keep a cache of RSA keys and their RSA NativeKey for reuse.
83    // When the RSA key is gc'ed, we let NativeKey phatom references cleanup
84    // the native allocation
85    private static final Map<Key, NativeKey> keyList =
86            Collections.synchronizedMap(new WeakHashMap<Key, NativeKey>());
87
88    //
89    // fields (re)set in every init()
90    //
91    private NativeKey key = null;
92    private int outputSize = 0; // e.g. modulus size in bytes
93    private boolean encrypt = true;
94    private byte[] buffer;
95    private int bufOfs = 0;
96
97    // public implementation classes
98    public static final class NoPadding extends NativeRSACipher {
99        public NoPadding() throws NoSuchAlgorithmException {
100            super(UcryptoMech.CRYPTO_RSA_X_509, 0);
101        }
102    }
103
104    public static final class PKCS1Padding extends NativeRSACipher {
105        public PKCS1Padding() throws NoSuchAlgorithmException {
106            super(UcryptoMech.CRYPTO_RSA_PKCS, 11);
107        }
108    }
109
110    NativeRSACipher(UcryptoMech mech, int padLen)
111        throws NoSuchAlgorithmException {
112        this.mech = mech;
113        this.padLen = padLen;
114        this.keyFactory = new NativeRSAKeyFactory();
115    }
116
117    @Override
118    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
119        // Disallow change of mode for now since currently it's explicitly
120        // defined in transformation strings
121        throw new NoSuchAlgorithmException("Unsupported mode " + mode);
122    }
123
124    // see JCE spec
125    @Override
126    protected void engineSetPadding(String padding)
127            throws NoSuchPaddingException {
128        // Disallow change of padding for now since currently it's explicitly
129        // defined in transformation strings
130        throw new NoSuchPaddingException("Unsupported padding " + padding);
131    }
132
133    // see JCE spec
134    @Override
135    protected int engineGetBlockSize() {
136        return 0;
137    }
138
139    // see JCE spec
140    @Override
141    protected synchronized int engineGetOutputSize(int inputLen) {
142        return outputSize;
143    }
144
145    // see JCE spec
146    @Override
147    protected byte[] engineGetIV() {
148        return null;
149    }
150
151    // see JCE spec
152    @Override
153    protected AlgorithmParameters engineGetParameters() {
154        return null;
155    }
156
157    @Override
158    protected int engineGetKeySize(Key key) throws InvalidKeyException {
159        if (!(key instanceof RSAKey)) {
160            throw new InvalidKeyException("RSAKey required");
161        }
162        int n = ((RSAKey)key).getModulus().bitLength();
163        // strip off the leading extra 0x00 byte prefix
164        int realByteSize = (n + 7) >> 3;
165        return realByteSize * 8;
166    }
167
168    // see JCE spec
169    @Override
170    protected synchronized void engineInit(int opmode, Key key, SecureRandom random)
171            throws InvalidKeyException {
172        try {
173            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
174        } catch (InvalidAlgorithmParameterException e) {
175            throw new InvalidKeyException("init() failed", e);
176        }
177    }
178
179    // see JCE spec
180    @Override
181    protected synchronized void engineInit(int opmode, Key newKey,
182            AlgorithmParameterSpec params, SecureRandom random)
183            throws InvalidKeyException, InvalidAlgorithmParameterException {
184        if (newKey == null) {
185            throw new InvalidKeyException("Key cannot be null");
186        }
187        if (opmode != Cipher.ENCRYPT_MODE &&
188            opmode != Cipher.DECRYPT_MODE &&
189            opmode != Cipher.WRAP_MODE &&
190            opmode != Cipher.UNWRAP_MODE) {
191            throw new InvalidAlgorithmParameterException
192                ("Unsupported mode: " + opmode);
193        }
194        if (params != null) {
195            if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
196                throw new InvalidAlgorithmParameterException(
197                        "No Parameters can be specified");
198            }
199            spec = params;
200            this.random = random;   // for TLS RSA premaster secret
201        }
202        boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE);
203
204        // Make sure the proper opmode uses the proper key
205        if (doEncrypt && (!(newKey instanceof RSAPublicKey))) {
206            throw new InvalidKeyException("RSAPublicKey required for encryption");
207        } else if (!doEncrypt && (!(newKey instanceof RSAPrivateCrtKey))) {
208            throw new InvalidKeyException("RSAPrivateCrtKey required for decryption");
209        }
210
211        NativeKey nativeKey = null;
212        // Check keyList cache for a nativeKey
213        nativeKey = keyList.get(newKey);
214        if (nativeKey == null) {
215            // With no existing nativeKey for this newKey, create one
216            if (doEncrypt) {
217                RSAPublicKey publicKey = (RSAPublicKey) newKey;
218                try {
219                    nativeKey = (NativeKey) keyFactory.engineGeneratePublic
220                        (new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getPublicExponent()));
221                } catch (InvalidKeySpecException ikse) {
222                    throw new InvalidKeyException(ikse);
223                }
224            } else {
225                RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) newKey;
226                try {
227                    nativeKey = (NativeKey) keyFactory.engineGeneratePrivate
228                        (new RSAPrivateCrtKeySpec(privateKey.getModulus(),
229                                                  privateKey.getPublicExponent(),
230                                                  privateKey.getPrivateExponent(),
231                                                  privateKey.getPrimeP(),
232                                                  privateKey.getPrimeQ(),
233                                                  privateKey.getPrimeExponentP(),
234                                                  privateKey.getPrimeExponentQ(),
235                                                  privateKey.getCrtCoefficient()));
236                } catch (InvalidKeySpecException ikse) {
237                    throw new InvalidKeyException(ikse);
238                }
239            }
240
241            // Add nativeKey to keyList cache and associate it with newKey
242            keyList.put(newKey, nativeKey);
243        }
244
245        init(doEncrypt, nativeKey);
246    }
247
248    // see JCE spec
249    @Override
250    protected synchronized void engineInit(int opmode, Key key, AlgorithmParameters params,
251            SecureRandom random)
252            throws InvalidKeyException, InvalidAlgorithmParameterException {
253        if (params != null) {
254            throw new InvalidAlgorithmParameterException("No Parameters can be specified");
255        }
256        engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
257    }
258
259    // see JCE spec
260    @Override
261    protected synchronized byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
262        if (inLen > 0) {
263            update(in, inOfs, inLen);
264        }
265        return null;
266    }
267
268    // see JCE spec
269    @Override
270    protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
271            int outOfs) throws ShortBufferException {
272        if (out.length - outOfs < outputSize) {
273            throw new ShortBufferException("Output buffer too small");
274        }
275        if (inLen > 0) {
276            update(in, inOfs, inLen);
277        }
278        return 0;
279    }
280
281    // see JCE spec
282    @Override
283    protected synchronized byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
284            throws IllegalBlockSizeException, BadPaddingException {
285        byte[] out = new byte[outputSize];
286        try {
287            // delegate to the other engineDoFinal(...) method
288            int actualLen = engineDoFinal(in, inOfs, inLen, out, 0);
289            if (actualLen != outputSize) {
290                return Arrays.copyOf(out, actualLen);
291            } else {
292                return out;
293            }
294        } catch (ShortBufferException e) {
295            throw new UcryptoException("Internal Error", e);
296        }
297    }
298
299    // see JCE spec
300    @Override
301    protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
302                                             int outOfs)
303        throws ShortBufferException, IllegalBlockSizeException,
304               BadPaddingException {
305        if (inLen != 0) {
306            update(in, inOfs, inLen);
307        }
308        return doFinal(out, outOfs, out.length - outOfs);
309    }
310
311
312    // see JCE spec
313    @Override
314    protected synchronized byte[] engineWrap(Key key) throws IllegalBlockSizeException,
315                                                             InvalidKeyException {
316        try {
317            byte[] encodedKey = key.getEncoded();
318            if ((encodedKey == null) || (encodedKey.length == 0)) {
319                throw new InvalidKeyException("Cannot get an encoding of " +
320                                              "the key to be wrapped");
321            }
322            if (encodedKey.length > buffer.length) {
323                throw new InvalidKeyException("Key is too long for wrapping");
324            }
325            return engineDoFinal(encodedKey, 0, encodedKey.length);
326        } catch (BadPaddingException e) {
327            // Should never happen for key wrapping
328            throw new UcryptoException("Internal Error", e);
329        }
330    }
331
332    // see JCE spec
333    @Override
334    protected synchronized Key engineUnwrap(byte[] wrappedKey,
335            String wrappedKeyAlgorithm, int wrappedKeyType)
336            throws InvalidKeyException, NoSuchAlgorithmException {
337
338        if (wrappedKey.length > buffer.length) {
339            throw new InvalidKeyException("Key is too long for unwrapping");
340        }
341
342        boolean isTlsRsaPremasterSecret =
343                wrappedKeyAlgorithm.equals("TlsRsaPremasterSecret");
344        Exception failover = null;
345
346        byte[] encodedKey = null;
347        try {
348            encodedKey = engineDoFinal(wrappedKey, 0, wrappedKey.length);
349        } catch (BadPaddingException bpe) {
350            if (isTlsRsaPremasterSecret) {
351                failover = bpe;
352            } else {
353                throw new InvalidKeyException("Unwrapping failed", bpe);
354            }
355        } catch (Exception e) {
356            throw new InvalidKeyException("Unwrapping failed", e);
357        }
358
359        if (isTlsRsaPremasterSecret) {
360            if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
361                throw new IllegalStateException(
362                        "No TlsRsaPremasterSecretParameterSpec specified");
363            }
364
365            // polish the TLS premaster secret
366            encodedKey = KeyUtil.checkTlsPreMasterSecretKey(
367                ((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(),
368                ((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(),
369                random, encodedKey, (failover != null));
370        }
371
372        return NativeCipher.constructKey(wrappedKeyType,
373                encodedKey, wrappedKeyAlgorithm);
374    }
375
376    /**
377     * calls ucrypto_encrypt(...) or ucrypto_decrypt(...)
378     * @returns the length of output or an negative error status code
379     */
380    private native static int nativeAtomic(int mech, boolean encrypt,
381                                           long keyValue, int keyLength,
382                                           byte[] in, int inLen,
383                                           byte[] out, int ouOfs, int outLen);
384
385    // do actual initialization
386    private void init(boolean encrypt, NativeKey key) {
387        this.encrypt = encrypt;
388        this.key = key;
389        try {
390            this.outputSize = engineGetKeySize(key)/8;
391        } catch (InvalidKeyException ike) {
392            throw new UcryptoException("Internal Error", ike);
393        }
394        this.buffer = new byte[outputSize];
395        this.bufOfs = 0;
396    }
397
398    // store the specified input into the internal buffer
399    private void update(byte[] in, int inOfs, int inLen) {
400        if ((inLen <= 0) || (in == null)) {
401            return;
402        }
403        // buffer bytes internally until doFinal is called
404        if ((bufOfs + inLen + (encrypt? padLen:0)) > buffer.length) {
405            // lead to IllegalBlockSizeException when doFinal() is called
406            bufOfs = buffer.length + 1;
407            return;
408        }
409        System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
410        bufOfs += inLen;
411    }
412
413    // return the actual non-negative output length
414    private int doFinal(byte[] out, int outOfs, int outLen)
415            throws ShortBufferException, IllegalBlockSizeException,
416            BadPaddingException {
417        if (bufOfs > buffer.length) {
418            throw new IllegalBlockSizeException(
419                "Data must not be longer than " +
420                (buffer.length - (encrypt ? padLen : 0)) + " bytes");
421        }
422        if (outLen < outputSize) {
423            throw new ShortBufferException();
424        }
425        try {
426            long keyValue = key.value();
427            int k = nativeAtomic(mech.value(), encrypt, keyValue,
428                                 key.length(), buffer, bufOfs,
429                                 out, outOfs, outLen);
430            if (k < 0) {
431                if ( k == -16 || k == -64) {
432                    // -16: CRYPTO_ENCRYPTED_DATA_INVALID
433                    // -64: CKR_ENCRYPTED_DATA_INVALID, see bug 17459266
434                    UcryptoException ue = new UcryptoException(16);
435                    BadPaddingException bpe =
436                        new BadPaddingException("Invalid encryption data");
437                    bpe.initCause(ue);
438                    throw bpe;
439                }
440                throw new UcryptoException(-k);
441            }
442
443            return k;
444        } finally {
445            bufOfs = 0;
446        }
447    }
448}
449