001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.crypto.impl;
019
020
021import java.security.InvalidKeyException;
022import java.security.NoSuchAlgorithmException;
023import java.security.Provider;
024import javax.crypto.Cipher;
025import javax.crypto.IllegalBlockSizeException;
026import javax.crypto.NoSuchPaddingException;
027import javax.crypto.SecretKey;
028
029import com.nimbusds.jose.JOSEException;
030import com.nimbusds.jose.util.KeyUtils;
031import net.jcip.annotations.ThreadSafe;
032
033
034/**
035 * AES key Wrapping methods for Content Encryption Key (CEK) encryption and
036 * decryption. This class is thread-safe.
037 *
038 * <p>See RFC 7518 (JWA), section 4.4.
039 *
040 * @author Melisa Halsband
041 * @author Vladimir Dzhuvinov
042 * @version 2018-03-09
043 */
044@ThreadSafe
045public class AESKW {
046
047
048        /**
049         * Wraps the specified Content Encryption Key (CEK).
050         *
051         * @param cek      The Content Encryption Key (CEK) to wrap. Must not
052         *                 be {@code null}.
053         * @param kek      The AES Key Encryption Key (KEK) (wrapping key).
054         *                 Must not be {@code null}.
055         * @param provider The specific JCA provider to use, {@code null}
056         *                 implies the default system one.
057         *
058         * @return The wrapped Content Encryption Key (CEK).
059         *
060         * @throws JOSEException If wrapping failed.
061         */
062        public static byte[] wrapCEK(final SecretKey cek,
063                                     final SecretKey kek,
064                                     final Provider provider)
065                throws JOSEException {
066
067                try {
068                        Cipher cipher;
069
070                        if (provider != null) {
071                                cipher = Cipher.getInstance("AESWrap", provider);
072                        } else {
073                                cipher = Cipher.getInstance("AESWrap");
074                        }
075
076                        cipher.init(Cipher.WRAP_MODE, kek);
077                        return cipher.wrap(cek);
078
079                } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException e) {
080                        throw new JOSEException("Couldn't wrap AES key: " + e.getMessage(), e);
081                }
082        }
083
084
085        /**
086         * Unwraps the specified encrypted Content Encryption Key (CEK).
087         *
088         * @param kek          The AES Key Encryption Key (KEK) (wrapping key).
089         *                     Must not be {@code null}.
090         * @param encryptedCEK The wrapped Content Encryption Key (CEK) with
091         *                     authentication tag. Must not be {@code null}.
092         * @param provider     The specific JCA provider to use, {@code null}
093         *                     implies the default system one.
094         *
095         * @return The unwrapped Content Encryption Key (CEK).
096         *
097         * @throws JOSEException If unwrapping failed.
098         */
099        public static SecretKey unwrapCEK(final SecretKey kek,
100                                          final byte[] encryptedCEK,
101                                          final Provider provider)
102                throws JOSEException {
103
104                try {
105                        Cipher cipher;
106
107                        if (provider != null) {
108                                cipher = Cipher.getInstance("AESWrap", provider);
109                        } else {
110                                cipher = Cipher.getInstance("AESWrap");
111                        }
112
113                        cipher.init(Cipher.UNWRAP_MODE, KeyUtils.toAESKey(kek)); // Make sure key alg is "AES"
114                        return (SecretKey)cipher.unwrap(encryptedCEK, "AES", Cipher.SECRET_KEY);
115
116                } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
117
118                        throw new JOSEException("Couldn't unwrap AES key: " + e.getMessage(), e);
119                }
120        }
121
122
123        /**
124         * Prevents public instantiation.
125         */
126        private AESKW() {
127        }
128}