001/* 002 * oauth2-oidc-sdk 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.openid.connect.sdk.id; 019 020 021import javax.crypto.Cipher; 022import javax.crypto.SecretKey; 023import javax.crypto.spec.IvParameterSpec; 024 025import com.nimbusds.jose.util.Base64URL; 026import com.nimbusds.oauth2.sdk.id.Subject; 027import net.jcip.annotations.ThreadSafe; 028import org.apache.commons.lang3.tuple.ImmutablePair; 029import org.apache.commons.lang3.tuple.Pair; 030 031 032/** 033 * AES/CBC/PKCS5Padding based encoder / decoder of pairwise subject 034 * identifiers. The salt is used as the IV. Reversal is supported. 035 * 036 * <p><strong>Warning: </strong> This codec is deprecated. Use 037 * {@link SIVAESBasedPairwiseSubjectCodec} instead. 038 * 039 * <p>The plain text is formatted as follows ('|' as delimiter): 040 * 041 * <pre> 042 * sector_id|local_sub 043 * </pre> 044 * 045 * <p>Related specifications: 046 * 047 * <ul> 048 * <li>OpenID Connect Core 1.0, section 8.1. 049 * </ul> 050 */ 051@ThreadSafe 052@Deprecated 053public class AESBasedPairwiseSubjectCodec extends PairwiseSubjectCodec { 054 055 056 /** 057 * The AES key. 058 */ 059 private final SecretKey aesKey; 060 061 062 /** 063 * Creates a new AES-based codec for pairwise subject identifiers. 064 * 065 * @param aesKey The AES key. Must not be {@code null}. 066 * @param salt The salt. Must not be {@code null}. 067 */ 068 public AESBasedPairwiseSubjectCodec(final SecretKey aesKey, final byte[] salt) { 069 super(salt); 070 if (salt == null) { 071 throw new IllegalArgumentException("The salt must not be null"); 072 } 073 if (aesKey == null) { 074 throw new IllegalArgumentException("The AES key must not be null"); 075 } 076 this.aesKey = aesKey; 077 } 078 079 080 /** 081 * Returns the AES key. 082 * 083 * @return The key. 084 */ 085 public SecretKey getAESKey() { 086 return aesKey; 087 } 088 089 090 /** 091 * Creates a new AES/CBC/PKCS5Padding cipher using the configured 092 * JCE provider and salt. 093 * 094 * @param mode The cipher mode. 095 * 096 * @return The cipher. 097 */ 098 private Cipher createCipher(final int mode) { 099 100 Cipher aesCipher; 101 102 try { 103 if (getProvider() != null) { 104 aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", getProvider()); 105 } else { 106 aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 107 } 108 109 aesCipher.init(mode, aesKey, new IvParameterSpec(getSalt())); 110 } catch (Exception e) { 111 throw new RuntimeException(e); 112 } 113 114 return aesCipher; 115 } 116 117 118 @Override 119 public Subject encode(final SectorID sectorID, final Subject localSub) { 120 121 // Join parameters, delimited by '\' 122 byte[] plainText = (sectorID.getValue().replace("|", "\\|") + '|' + localSub.getValue().replace("|", "\\|")).getBytes(CHARSET); 123 byte[] cipherText; 124 try { 125 cipherText = createCipher(Cipher.ENCRYPT_MODE).doFinal(plainText); 126 } catch (Exception e) { 127 throw new RuntimeException(e); 128 } 129 130 return new Subject(Base64URL.encode(cipherText).toString()); 131 } 132 133 134 @Override 135 public Pair<SectorID, Subject> decode(final Subject pairwiseSubject) 136 throws InvalidPairwiseSubjectException { 137 138 byte[] cipherText = new Base64URL(pairwiseSubject.getValue()).decode(); 139 140 Cipher aesCipher = createCipher(Cipher.DECRYPT_MODE); 141 142 byte[] plainText; 143 try { 144 plainText = aesCipher.doFinal(cipherText); 145 } catch (Exception e) { 146 throw new InvalidPairwiseSubjectException("Decryption failed: " + e.getMessage(), e); 147 } 148 149 String parts[] = new String(plainText, CHARSET).split("(?<!\\\\)\\|"); 150 151 // Unescape delimiter 152 for (int i=0; i<parts.length; i++) { 153 parts[i] = parts[i].replace("\\|", "|"); 154 } 155 156 // Check format 157 if (parts.length != 2) { 158 throw new InvalidPairwiseSubjectException("Invalid format: Unexpected number of tokens: " + parts.length); 159 } 160 161 return new ImmutablePair<>(new SectorID(parts[0]), new Subject(parts[1])); 162 } 163}