├── .gitignore ├── .mvn ├── jvm.config ├── maven.config └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .vscode └── settings.json ├── README.md ├── license.txt ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── org │ └── springframework │ └── security │ └── rsa │ └── crypto │ ├── CipherUtils.java │ ├── KeyStoreKeyFactory.java │ ├── RsaAlgorithm.java │ ├── RsaKeyHelper.java │ ├── RsaKeyHolder.java │ ├── RsaRawEncryptor.java │ └── RsaSecretEncryptor.java └── test ├── java └── org │ └── springframework │ └── security │ └── rsa │ └── crypto │ ├── KeyStoreKeyFactoryTests.java │ ├── RsaKeyHelperTests.java │ ├── RsaRawEncryptorTests.java │ └── RsaSecretEncryptorTests.java └── resources ├── bad.pem ├── fake.pem ├── keystore.jks ├── keystore.pkcs12 └── spacey.pem /.gitignore: -------------------------------------------------------------------------------- 1 | /application.yml 2 | /application.properties 3 | *~ 4 | .#* 5 | *# 6 | target 7 | bin 8 | .classpath 9 | .project 10 | .settings 11 | .springBeans 12 | .DS_Store 13 | *.sw* 14 | *.iml 15 | .idea 16 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | -Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/spring-security-rsa/8c6649bf3400c6b9e7f1db593cb4b9239aab4f9e/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This little project provides some RSA extensions to the base 2 | [spring-security-crypto](https://github.com/spring-projects/spring-security/tree/master/crypto) 3 | library. Currently supported: encryption and decryption with 2 4 | algorithms wrapped up in the Spring Security Crypto interfaces 5 | `TextEncryptor` and `BytesEncryptor`. Example round trip: 6 | 7 | ```java 8 | TextEncryptor encryptor = new RsaSecretEncryptor(); 9 | String cipher = encryptor.encrypt("my message"); 10 | String message = encryptor.decrypt(cipher); 11 | ``` 12 | 13 | Above we create an encryptor with a random RSA key (the default 14 | constructor), and use it to encrypt and then decrypt a message. the 15 | default constructor is useful for testing, but for more durable use 16 | cases you can inject a private key or a `KeyPair` using the other 17 | constructors. 18 | 19 | The encryption algorithm in the `RsaSecretEncryptor` is to generate a 20 | random 16-byte password, and use that to encrypt the message. The 21 | password is then itself RSA encrypted and prepended to the cipher 22 | text. The cipher test is base64 encoded (if using the `TextEncryptor` 23 | interface). 24 | 25 | The other algorithm is in the `RsaRawEncryptor` which does raw RSA 26 | encryption on the whole message. We recommend the 27 | `RsaSecretEncryptor`. 28 | 29 | N.B. if you need RSA signing and verification there are utilities 30 | already available in 31 | [spring-security-jwt](https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-jwt). 32 | 33 | **Important Prerequisites:** to use Spring Security RSA 34 | you need the full-strength JCE installed in your JVM (it's not there by default). 35 | You can download the "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" 36 | from Oracle, and follow instructions for installation (essentially replace the 2 policy files 37 | in the JRE lib/security directory with the ones that you downloaded). 38 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | echo "Running version check" 230 | VERSION=$( sed '\!//' -e 's!.*$!!' ) 231 | echo "The found version is [${VERSION}]" 232 | 233 | if echo $VERSION | egrep -q 'M|RC'; then 234 | echo Activating \"milestone\" profile for version=\"$VERSION\" 235 | echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone" 236 | else 237 | echo Deactivating \"milestone\" profile for version=\"$VERSION\" 238 | echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') 239 | fi 240 | 241 | if echo $VERSION | egrep -q '[0-9]*\.[0-9]*\.[0-9]*$|RELEASE'; then 242 | echo Activating \"central\" profile for version=\"$VERSION\" 243 | echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" 244 | else 245 | echo Deactivating \"central\" profile for version=\"$VERSION\" 246 | echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') 247 | fi 248 | 249 | exec "$JAVACMD" \ 250 | $MAVEN_OPTS \ 251 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 252 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 253 | ${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@" 254 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% 146 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | org.springframework.security 7 | spring-security-rsa 8 | 1.1.6-SNAPSHOT 9 | jar 10 | spring-security-rsa 11 | 12 | Spring Security RSA is a small utility library for RSA ciphers. 13 | It belongs to the family of Spring Security crypto libraries that handle encoding and decoding text as 14 | a general, useful thing to be able to do. 15 | 16 | http://github.com/spring-projects/spring-security-oauth 17 | 18 | SpringSource 19 | http://www.springsource.com 20 | 21 | 22 | 23 | Apache 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | 26 | 27 | 28 | 29 | UTF-8 30 | 8 31 | 32 | 33 | 34 | 35 | org.springframework.security 36 | spring-security-crypto 37 | 5.8.4 38 | 39 | 40 | 41 | org.springframework 42 | spring-core 43 | 5.3.28 44 | 45 | 46 | 47 | org.bouncycastle 48 | bcprov-jdk18on 49 | 1.78 50 | 51 | 52 | 53 | junit 54 | junit 55 | 4.13.1 56 | test 57 | 58 | 59 | 60 | org.mockito 61 | mockito-all 62 | 1.10.19 63 | test 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-surefire-plugin 74 | 2.22.2 75 | 76 | ${skipTests} 77 | 78 | **/*Tests.java 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-compiler-plugin 89 | 3.10.1 90 | 91 | ${java.version} 92 | ${java.version} 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-source-plugin 98 | 3.2.1 99 | 100 | 101 | attach-sources 102 | package 103 | 104 | jar 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-javadoc-plugin 112 | 3.4.0 113 | 114 | 115 | javadoc 116 | 117 | jar 118 | 119 | package 120 | 121 | 122 | 123 | false 124 | ${java.version} 125 | none 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | static.springframework.org 134 | scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-security/rsa 135 | 136 | 137 | repo.spring.io 138 | Spring Release Repository 139 | https://repo.spring.io/libs-release-local 140 | 141 | 142 | repo.spring.io 143 | Spring Snapshot Repository 144 | https://repo.spring.io/libs-snapshot-local 145 | 146 | 147 | 148 | 149 | 150 | central 151 | 152 | 153 | sonatype-nexus-snapshots 154 | Sonatype Nexus Snapshots 155 | https://s01.oss.sonatype.org/content/repositories/snapshots/ 156 | 157 | 158 | sonatype-nexus-staging 159 | Nexus Release Repository 160 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 161 | 162 | 163 | 164 | 165 | 166 | org.apache.maven.plugins 167 | maven-gpg-plugin 168 | 169 | 170 | sign-artifacts 171 | verify 172 | 173 | sign 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | http://github.com/spring-projects/spring-security-rsa 185 | scm:git:git://github.com/spring-projects/spring-security-rsa.git 186 | scm:git:ssh://git@github.com/spring-projects/spring-security-rsa.git 187 | 188 | 189 | 190 | 191 | dsyer 192 | Dave Syer 193 | dsyer@gopivotal.com 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/CipherUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.security.InvalidAlgorithmParameterException; 19 | import java.security.InvalidKeyException; 20 | import java.security.NoSuchAlgorithmException; 21 | import java.security.spec.AlgorithmParameterSpec; 22 | import java.security.spec.InvalidKeySpecException; 23 | import java.security.spec.InvalidParameterSpecException; 24 | 25 | import javax.crypto.BadPaddingException; 26 | import javax.crypto.Cipher; 27 | import javax.crypto.IllegalBlockSizeException; 28 | import javax.crypto.NoSuchPaddingException; 29 | import javax.crypto.SecretKey; 30 | import javax.crypto.SecretKeyFactory; 31 | import javax.crypto.spec.PBEKeySpec; 32 | import javax.crypto.spec.PBEParameterSpec; 33 | 34 | /** 35 | * Static helper for working with the Cipher API. 36 | * @author Keith Donald 37 | */ 38 | class CipherUtils { 39 | 40 | /** 41 | * Generates a SecretKey. 42 | */ 43 | public static SecretKey newSecretKey(String algorithm, String password) { 44 | return newSecretKey(algorithm, new PBEKeySpec(password.toCharArray())); 45 | } 46 | 47 | /** 48 | * Generates a SecretKey. 49 | */ 50 | public static SecretKey newSecretKey(String algorithm, PBEKeySpec keySpec) { 51 | try { 52 | SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm); 53 | return factory.generateSecret(keySpec); 54 | } 55 | catch (NoSuchAlgorithmException e) { 56 | throw new IllegalArgumentException("Not a valid encryption algorithm", e); 57 | } 58 | catch (InvalidKeySpecException e) { 59 | throw new IllegalArgumentException("Not a valid secret key", e); 60 | } 61 | } 62 | 63 | /** 64 | * Constructs a new Cipher. 65 | */ 66 | public static Cipher newCipher(String algorithm) { 67 | try { 68 | return Cipher.getInstance(algorithm); 69 | } 70 | catch (NoSuchAlgorithmException e) { 71 | throw new IllegalArgumentException("Not a valid encryption algorithm", e); 72 | } 73 | catch (NoSuchPaddingException e) { 74 | throw new IllegalStateException("Should not happen", e); 75 | } 76 | } 77 | 78 | /** 79 | * Initializes the Cipher for use. 80 | */ 81 | public static T getParameterSpec(Cipher cipher, 82 | Class parameterSpecClass) { 83 | try { 84 | return cipher.getParameters().getParameterSpec(parameterSpecClass); 85 | } 86 | catch (InvalidParameterSpecException e) { 87 | throw new IllegalArgumentException("Unable to access parameter", e); 88 | } 89 | } 90 | 91 | /** 92 | * Initializes the Cipher for use. 93 | */ 94 | public static void initCipher(Cipher cipher, int mode, SecretKey secretKey) { 95 | initCipher(cipher, mode, secretKey, null); 96 | } 97 | 98 | /** 99 | * Initializes the Cipher for use. 100 | */ 101 | public static void initCipher(Cipher cipher, int mode, SecretKey secretKey, 102 | byte[] salt, int iterationCount) { 103 | initCipher(cipher, mode, secretKey, new PBEParameterSpec(salt, iterationCount)); 104 | } 105 | 106 | /** 107 | * Initializes the Cipher for use. 108 | */ 109 | public static void initCipher(Cipher cipher, int mode, SecretKey secretKey, 110 | AlgorithmParameterSpec parameterSpec) { 111 | try { 112 | if (parameterSpec != null) { 113 | cipher.init(mode, secretKey, parameterSpec); 114 | } 115 | else { 116 | cipher.init(mode, secretKey); 117 | } 118 | } 119 | catch (InvalidKeyException e) { 120 | throw new IllegalArgumentException( 121 | "Unable to initialize due to invalid secret key", e); 122 | } 123 | catch (InvalidAlgorithmParameterException e) { 124 | throw new IllegalStateException( 125 | "Unable to initialize due to invalid decryption parameter spec", e); 126 | } 127 | } 128 | 129 | /** 130 | * Invokes the Cipher to perform encryption or decryption (depending on the 131 | * initialized mode). 132 | */ 133 | public static byte[] doFinal(Cipher cipher, byte[] input) { 134 | try { 135 | return cipher.doFinal(input); 136 | } 137 | catch (IllegalBlockSizeException e) { 138 | throw new IllegalStateException( 139 | "Unable to invoke Cipher due to illegal block size", e); 140 | } 141 | catch (BadPaddingException e) { 142 | throw new IllegalStateException("Unable to invoke Cipher due to bad padding", 143 | e); 144 | } 145 | } 146 | 147 | private CipherUtils() { 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/KeyStoreKeyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.io.InputStream; 19 | import java.security.KeyFactory; 20 | import java.security.KeyPair; 21 | import java.security.KeyStore; 22 | import java.security.PublicKey; 23 | import java.security.cert.Certificate; 24 | import java.security.interfaces.RSAPrivateCrtKey; 25 | import java.security.spec.RSAPublicKeySpec; 26 | 27 | import org.springframework.core.io.Resource; 28 | import org.springframework.util.StringUtils; 29 | 30 | /** 31 | * @author Dave Syer 32 | * @author Tim Ysewyn 33 | * 34 | */ 35 | public class KeyStoreKeyFactory { 36 | 37 | private Resource resource; 38 | private char[] password; 39 | private KeyStore store; 40 | private Object lock = new Object(); 41 | private String type; 42 | 43 | public KeyStoreKeyFactory(Resource resource, char[] password) { 44 | this(resource, password, type(resource)); 45 | } 46 | 47 | private static String type(Resource resource) { 48 | String ext = StringUtils.getFilenameExtension(resource.getFilename()); 49 | return ext == null ? "jks" : ext; 50 | } 51 | 52 | public KeyStoreKeyFactory(Resource resource, char[] password, String type) { 53 | this.resource = resource; 54 | this.password = password; 55 | this.type = type; 56 | } 57 | 58 | public KeyPair getKeyPair(String alias) { 59 | return getKeyPair(alias, password); 60 | } 61 | 62 | public KeyPair getKeyPair(String alias, char[] password) { 63 | try { 64 | synchronized (lock) { 65 | if (store == null) { 66 | synchronized (lock) { 67 | store = KeyStore.getInstance(type); 68 | InputStream stream = resource.getInputStream(); 69 | try { 70 | store.load(stream, this.password); 71 | } 72 | finally { 73 | if (stream != null) { 74 | stream.close(); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | RSAPrivateCrtKey key = (RSAPrivateCrtKey) store.getKey(alias, password); 81 | Certificate certificate = store.getCertificate(alias); 82 | PublicKey publicKey = null; 83 | if (certificate != null) { 84 | publicKey = certificate.getPublicKey(); 85 | } else if (key != null) { 86 | RSAPublicKeySpec spec = new RSAPublicKeySpec(key.getModulus(), 87 | key.getPublicExponent()); 88 | publicKey = KeyFactory.getInstance("RSA").generatePublic(spec); 89 | } 90 | return new KeyPair(publicKey, key); 91 | } 92 | catch (Exception e) { 93 | throw new IllegalStateException("Cannot load keys from store: " + resource, 94 | e); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/RsaAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.security.rsa.crypto; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public enum RsaAlgorithm { 24 | 25 | DEFAULT("RSA", 117), OAEP("RSA/ECB/OAEPPadding", 86); 26 | 27 | private String name; 28 | private int maxLength; 29 | 30 | private RsaAlgorithm(String name, int maxLength) { 31 | this.name = name; 32 | this.maxLength = maxLength; 33 | } 34 | 35 | public String getJceName() { 36 | return this.name; 37 | } 38 | 39 | public int getMaxLength() { 40 | return this.maxLength; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/RsaKeyHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.StringWriter; 22 | import java.math.BigInteger; 23 | import java.nio.ByteBuffer; 24 | import java.nio.CharBuffer; 25 | import java.nio.charset.CharacterCodingException; 26 | import java.nio.charset.Charset; 27 | import java.security.KeyFactory; 28 | import java.security.KeyPair; 29 | import java.security.KeyPairGenerator; 30 | import java.security.NoSuchAlgorithmException; 31 | import java.security.PrivateKey; 32 | import java.security.PublicKey; 33 | import java.security.interfaces.RSAPublicKey; 34 | import java.security.spec.InvalidKeySpecException; 35 | import java.security.spec.KeySpec; 36 | import java.security.spec.RSAPrivateCrtKeySpec; 37 | import java.security.spec.RSAPublicKeySpec; 38 | import java.security.spec.X509EncodedKeySpec; 39 | import java.util.Arrays; 40 | import java.util.Base64; 41 | import java.util.regex.Matcher; 42 | import java.util.regex.Pattern; 43 | 44 | import org.bouncycastle.asn1.ASN1Sequence; 45 | 46 | /** 47 | * Reads RSA key pairs using BC provider classes but without the need to specify 48 | * a crypto 49 | * provider or have BC added as one. 50 | * 51 | * @author Luke Taylor 52 | * @author Dave Syer 53 | */ 54 | class RsaKeyHelper { 55 | 56 | private static Charset UTF8 = Charset.forName("UTF-8"); 57 | 58 | private static final String BEGIN = "-----BEGIN"; 59 | private static final Pattern PEM_DATA = Pattern 60 | .compile(".*-----BEGIN (.*)-----(.*)-----END (.*)-----", Pattern.DOTALL); 61 | private static final byte[] PREFIX = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 62 | 's', 'a' }; 63 | 64 | static KeyPair parseKeyPair(String pemData) { 65 | Matcher m = PEM_DATA.matcher(pemData.replaceAll("\n *", "").trim()); 66 | 67 | if (!m.matches()) { 68 | try { 69 | RSAPublicKey publicValue = extractPublicKey(pemData); 70 | if (publicValue != null) { 71 | return new KeyPair(publicValue, null); 72 | } 73 | } catch (Exception e) { 74 | // Ignore 75 | } 76 | throw new IllegalArgumentException( 77 | "String is not PEM encoded data, nor a public key encoded for ssh"); 78 | } 79 | 80 | String type = m.group(1); 81 | final byte[] content = base64Decode(m.group(2)); 82 | 83 | PublicKey publicKey; 84 | PrivateKey privateKey = null; 85 | 86 | try { 87 | KeyFactory fact = KeyFactory.getInstance("RSA"); 88 | if (type.equals("RSA PRIVATE KEY")) { 89 | ASN1Sequence seq = ASN1Sequence.getInstance(content); 90 | if (seq.size() != 9) { 91 | throw new IllegalArgumentException( 92 | "Invalid RSA Private Key ASN1 sequence."); 93 | } 94 | org.bouncycastle.asn1.pkcs.RSAPrivateKey key = org.bouncycastle.asn1.pkcs.RSAPrivateKey 95 | .getInstance(seq); 96 | RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), 97 | key.getPublicExponent()); 98 | RSAPrivateCrtKeySpec privSpec = new RSAPrivateCrtKeySpec(key.getModulus(), 99 | key.getPublicExponent(), key.getPrivateExponent(), 100 | key.getPrime1(), key.getPrime2(), key.getExponent1(), 101 | key.getExponent2(), key.getCoefficient()); 102 | publicKey = fact.generatePublic(pubSpec); 103 | privateKey = fact.generatePrivate(privSpec); 104 | } else if (type.equals("PUBLIC KEY")) { 105 | KeySpec keySpec = new X509EncodedKeySpec(content); 106 | publicKey = fact.generatePublic(keySpec); 107 | } else if (type.equals("RSA PUBLIC KEY")) { 108 | ASN1Sequence seq = ASN1Sequence.getInstance(content); 109 | org.bouncycastle.asn1.pkcs.RSAPublicKey key = org.bouncycastle.asn1.pkcs.RSAPublicKey 110 | .getInstance(seq); 111 | RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), 112 | key.getPublicExponent()); 113 | publicKey = fact.generatePublic(pubSpec); 114 | } else { 115 | throw new IllegalArgumentException(type + " is not a supported format"); 116 | } 117 | 118 | return new KeyPair(publicKey, privateKey); 119 | } catch (InvalidKeySpecException e) { 120 | throw new RuntimeException(e); 121 | } catch (NoSuchAlgorithmException e) { 122 | throw new IllegalStateException(e); 123 | } 124 | } 125 | 126 | private static byte[] base64Decode(String string) { 127 | try { 128 | ByteBuffer bytes = UTF8.newEncoder().encode(CharBuffer.wrap(string)); 129 | byte[] bytesCopy = new byte[bytes.limit()]; 130 | System.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit()); 131 | return Base64.getDecoder().decode(bytesCopy); 132 | } catch (CharacterCodingException e) { 133 | throw new RuntimeException(e); 134 | } 135 | } 136 | 137 | static String base64Encode(byte[] bytes) { 138 | try { 139 | return UTF8.newDecoder().decode(ByteBuffer.wrap(Base64.getEncoder().encode(bytes))) 140 | .toString(); 141 | } catch (CharacterCodingException e) { 142 | throw new RuntimeException(e); 143 | } 144 | } 145 | 146 | static KeyPair generateKeyPair() { 147 | try { 148 | final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 149 | keyGen.initialize(1024); 150 | return keyGen.generateKeyPair(); 151 | } catch (NoSuchAlgorithmException e) { 152 | throw new IllegalStateException(e); 153 | } 154 | 155 | } 156 | 157 | private static final Pattern SSH_PUB_KEY = Pattern 158 | .compile("ssh-(rsa|dsa) ([A-Za-z0-9/+]+=*) (.*)"); 159 | 160 | private static RSAPublicKey extractPublicKey(String key) { 161 | 162 | Matcher m = SSH_PUB_KEY.matcher(key); 163 | 164 | if (m.matches()) { 165 | String alg = m.group(1); 166 | String encKey = m.group(2); 167 | // String id = m.group(3); 168 | 169 | if (!"rsa".equalsIgnoreCase(alg)) { 170 | throw new IllegalArgumentException( 171 | "Only RSA is currently supported, but algorithm was " + alg); 172 | } 173 | 174 | return parseSSHPublicKey(encKey); 175 | } else if (!key.startsWith(BEGIN)) { 176 | // Assume it's the plain Base64 encoded ssh key without the 177 | // "ssh-rsa" at the start 178 | return parseSSHPublicKey(key); 179 | } 180 | 181 | return null; 182 | } 183 | 184 | static RSAPublicKey parsePublicKey(String key) { 185 | 186 | RSAPublicKey publicKey = extractPublicKey(key); 187 | 188 | if (publicKey != null) { 189 | return publicKey; 190 | } 191 | 192 | KeyPair kp = parseKeyPair(key); 193 | 194 | if (kp.getPublic() == null) { 195 | throw new IllegalArgumentException("Key data does not contain a public key"); 196 | } 197 | 198 | return (RSAPublicKey) kp.getPublic(); 199 | 200 | } 201 | 202 | static String encodePublicKey(RSAPublicKey key, String id) { 203 | StringWriter output = new StringWriter(); 204 | output.append("ssh-rsa "); 205 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 206 | try { 207 | stream.write(PREFIX); 208 | writeBigInteger(stream, key.getPublicExponent()); 209 | writeBigInteger(stream, key.getModulus()); 210 | } catch (IOException e) { 211 | throw new IllegalStateException("Cannot encode key", e); 212 | } 213 | output.append(base64Encode(stream.toByteArray())); 214 | output.append(" " + id); 215 | return output.toString(); 216 | } 217 | 218 | private static RSAPublicKey parseSSHPublicKey(String encKey) { 219 | ByteArrayInputStream in = new ByteArrayInputStream(base64Decode(encKey)); 220 | 221 | byte[] prefix = new byte[11]; 222 | 223 | try { 224 | if (in.read(prefix) != 11 || !Arrays.equals(PREFIX, prefix)) { 225 | throw new IllegalArgumentException("SSH key prefix not found"); 226 | } 227 | 228 | BigInteger e = new BigInteger(readBigInteger(in)); 229 | BigInteger n = new BigInteger(readBigInteger(in)); 230 | 231 | return createPublicKey(n, e); 232 | } catch (IOException e) { 233 | throw new RuntimeException(e); 234 | } 235 | } 236 | 237 | static RSAPublicKey createPublicKey(BigInteger n, BigInteger e) { 238 | try { 239 | return (RSAPublicKey) KeyFactory.getInstance("RSA") 240 | .generatePublic(new RSAPublicKeySpec(n, e)); 241 | } catch (Exception ex) { 242 | throw new RuntimeException(ex); 243 | } 244 | } 245 | 246 | private static void writeBigInteger(ByteArrayOutputStream stream, BigInteger num) 247 | throws IOException { 248 | int length = num.toByteArray().length; 249 | byte[] data = new byte[4]; 250 | data[0] = (byte) ((length >> 24) & 0xFF); 251 | data[1] = (byte) ((length >> 16) & 0xFF); 252 | data[2] = (byte) ((length >> 8) & 0xFF); 253 | data[3] = (byte) (length & 0xFF); 254 | stream.write(data); 255 | stream.write(num.toByteArray()); 256 | } 257 | 258 | private static byte[] readBigInteger(ByteArrayInputStream in) throws IOException { 259 | byte[] b = new byte[4]; 260 | 261 | if (in.read(b) != 4) { 262 | throw new IOException("Expected length data as 4 bytes"); 263 | } 264 | 265 | int l = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) 266 | | (b[3] & 0xFF); 267 | 268 | b = new byte[l]; 269 | 270 | if (in.read(b) != l) { 271 | throw new IOException("Expected " + l + " key bytes"); 272 | } 273 | 274 | return b; 275 | } 276 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/RsaKeyHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | /** 19 | * @author Dave Syer 20 | * 21 | */ 22 | public interface RsaKeyHolder { 23 | 24 | String getPublicKey(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/RsaRawEncryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.nio.charset.Charset; 20 | import java.security.KeyPair; 21 | import java.security.PrivateKey; 22 | import java.security.PublicKey; 23 | import java.security.interfaces.RSAPrivateKey; 24 | import java.security.interfaces.RSAPublicKey; 25 | import java.util.Base64; 26 | 27 | import javax.crypto.Cipher; 28 | import sun.security.rsa.RSACore; 29 | 30 | import org.springframework.security.crypto.encrypt.BytesEncryptor; 31 | import org.springframework.security.crypto.encrypt.TextEncryptor; 32 | import org.springframework.util.Assert; 33 | 34 | /** 35 | * @author Dave Syer 36 | * 37 | */ 38 | public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder { 39 | 40 | private static final String DEFAULT_ENCODING = "UTF-8"; 41 | 42 | private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; 43 | 44 | private Charset charset; 45 | 46 | private RSAPublicKey publicKey; 47 | 48 | private RSAPrivateKey privateKey; 49 | 50 | private Charset defaultCharset; 51 | 52 | public RsaRawEncryptor(RsaAlgorithm algorithm) { 53 | this(RsaKeyHelper.generateKeyPair(), algorithm); 54 | } 55 | 56 | public RsaRawEncryptor() { 57 | this(RsaKeyHelper.generateKeyPair()); 58 | } 59 | 60 | public RsaRawEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) { 61 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm); 62 | } 63 | 64 | public RsaRawEncryptor(KeyPair keyPair) { 65 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate()); 66 | } 67 | 68 | public RsaRawEncryptor(String pemData) { 69 | this(RsaKeyHelper.parseKeyPair(pemData)); 70 | } 71 | 72 | public RsaRawEncryptor(PublicKey publicKey) { 73 | this(DEFAULT_ENCODING, publicKey, null); 74 | } 75 | 76 | public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) { 77 | this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT); 78 | } 79 | 80 | public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, 81 | RsaAlgorithm algorithm) { 82 | this.charset = Charset.forName(encoding); 83 | this.publicKey = (RSAPublicKey) publicKey; 84 | this.privateKey = (RSAPrivateKey) privateKey; 85 | this.defaultCharset = Charset.forName(DEFAULT_ENCODING); 86 | this.algorithm = algorithm; 87 | } 88 | 89 | @Override 90 | public String getPublicKey() { 91 | return RsaKeyHelper.encodePublicKey(this.publicKey, "application"); 92 | } 93 | 94 | @Override 95 | public String encrypt(String text) { 96 | return new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), 97 | this.defaultCharset); 98 | } 99 | 100 | @Override 101 | public String decrypt(String encryptedText) { 102 | Assert.state(this.privateKey != null, 103 | "Private key must be provided for decryption"); 104 | return new String(decrypt(Base64.getDecoder().decode(encryptedText 105 | .getBytes(this.defaultCharset))), this.charset); 106 | } 107 | 108 | @Override 109 | public byte[] encrypt(byte[] byteArray) { 110 | return encrypt(byteArray, this.publicKey, this.algorithm); 111 | } 112 | 113 | @Override 114 | public byte[] decrypt(byte[] encryptedByteArray) { 115 | return decrypt(encryptedByteArray, this.privateKey, this.algorithm); 116 | } 117 | 118 | private static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg) { 119 | ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); 120 | try { 121 | final Cipher cipher = Cipher.getInstance(alg.getJceName()); 122 | int limit = Math.min(text.length, alg.getMaxLength()); 123 | int pos = 0; 124 | while (pos < text.length) { 125 | cipher.init(Cipher.ENCRYPT_MODE, key); 126 | cipher.update(text, pos, limit); 127 | pos += limit; 128 | limit = Math.min(text.length - pos, alg.getMaxLength()); 129 | byte[] buffer = cipher.doFinal(); 130 | output.write(buffer, 0, buffer.length); 131 | } 132 | return output.toByteArray(); 133 | } 134 | catch (RuntimeException e) { 135 | throw e; 136 | } 137 | catch (Exception e) { 138 | throw new IllegalStateException("Cannot encrypt", e); 139 | } 140 | } 141 | 142 | private static byte[] decrypt(byte[] text, RSAPrivateKey key, RsaAlgorithm alg) { 143 | ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); 144 | try { 145 | final Cipher cipher = Cipher.getInstance(alg.getJceName()); 146 | int maxLength = RSACore.getByteLength(key); 147 | int pos = 0; 148 | while (pos < text.length) { 149 | int limit = Math.min(text.length - pos, maxLength); 150 | cipher.init(Cipher.DECRYPT_MODE, key); 151 | cipher.update(text, pos, limit); 152 | pos += limit; 153 | byte[] buffer = cipher.doFinal(); 154 | output.write(buffer, 0, buffer.length); 155 | } 156 | return output.toByteArray(); 157 | } 158 | catch (RuntimeException e) { 159 | throw e; 160 | } 161 | catch (Exception e) { 162 | throw new IllegalStateException("Cannot decrypt", e); 163 | } 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/security/rsa/crypto/RsaSecretEncryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.nio.charset.Charset; 22 | import java.security.KeyPair; 23 | import java.security.PrivateKey; 24 | import java.security.PublicKey; 25 | import java.security.interfaces.RSAPublicKey; 26 | import java.util.Base64; 27 | 28 | import javax.crypto.Cipher; 29 | 30 | import org.springframework.security.crypto.codec.Hex; 31 | import org.springframework.security.crypto.encrypt.BytesEncryptor; 32 | import org.springframework.security.crypto.encrypt.Encryptors; 33 | import org.springframework.security.crypto.encrypt.TextEncryptor; 34 | import org.springframework.security.crypto.keygen.KeyGenerators; 35 | import org.springframework.util.Assert; 36 | 37 | /** 38 | * @author Dave Syer 39 | * 40 | */ 41 | public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder { 42 | 43 | private static final String DEFAULT_ENCODING = "UTF-8"; 44 | 45 | // The secret for encryption is random (so dictionary attack is not a danger) 46 | private final static String DEFAULT_SALT = "deadbeef"; 47 | 48 | private String salt; 49 | 50 | private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; 51 | 52 | private Charset charset; 53 | 54 | private PublicKey publicKey; 55 | 56 | private PrivateKey privateKey; 57 | 58 | private Charset defaultCharset; 59 | 60 | private boolean gcm; 61 | 62 | public RsaSecretEncryptor(RsaAlgorithm algorithm, String salt, boolean gcm) { 63 | this(RsaKeyHelper.generateKeyPair(), algorithm, salt, gcm); 64 | } 65 | 66 | public RsaSecretEncryptor(RsaAlgorithm algorithm, String salt) { 67 | this(RsaKeyHelper.generateKeyPair(), algorithm, salt); 68 | } 69 | 70 | public RsaSecretEncryptor(RsaAlgorithm algorithm, boolean gcm) { 71 | this(RsaKeyHelper.generateKeyPair(), algorithm, DEFAULT_SALT, gcm); 72 | } 73 | 74 | public RsaSecretEncryptor(RsaAlgorithm algorithm) { 75 | this(RsaKeyHelper.generateKeyPair(), algorithm); 76 | } 77 | 78 | public RsaSecretEncryptor() { 79 | this(RsaKeyHelper.generateKeyPair()); 80 | } 81 | 82 | public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt, 83 | boolean gcm) { 84 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, 85 | salt, gcm); 86 | } 87 | 88 | public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt) { 89 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, 90 | salt, false); 91 | } 92 | 93 | public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) { 94 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm); 95 | } 96 | 97 | public RsaSecretEncryptor(KeyPair keyPair) { 98 | this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate()); 99 | } 100 | 101 | public RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm, String salt) { 102 | this(RsaKeyHelper.parseKeyPair(pemData), algorithm, salt); 103 | } 104 | 105 | public RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm) { 106 | this(RsaKeyHelper.parseKeyPair(pemData), algorithm); 107 | } 108 | 109 | public RsaSecretEncryptor(String pemData) { 110 | this(RsaKeyHelper.parseKeyPair(pemData)); 111 | } 112 | 113 | public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt, 114 | boolean gcm) { 115 | this(DEFAULT_ENCODING, publicKey, null, algorithm, salt, gcm); 116 | } 117 | 118 | public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt) { 119 | this(DEFAULT_ENCODING, publicKey, null, algorithm, salt, false); 120 | } 121 | 122 | public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm) { 123 | this(DEFAULT_ENCODING, publicKey, null, algorithm); 124 | } 125 | 126 | public RsaSecretEncryptor(PublicKey publicKey) { 127 | this(DEFAULT_ENCODING, publicKey, null); 128 | } 129 | 130 | public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) { 131 | this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT); 132 | } 133 | 134 | public RsaSecretEncryptor(String encoding, PublicKey publicKey, 135 | PrivateKey privateKey, RsaAlgorithm algorithm) { 136 | this(encoding, publicKey, privateKey, algorithm, DEFAULT_SALT, false); 137 | } 138 | 139 | public RsaSecretEncryptor(String encoding, PublicKey publicKey, 140 | PrivateKey privateKey, RsaAlgorithm algorithm, String salt, boolean gcm) { 141 | this.charset = Charset.forName(encoding); 142 | this.publicKey = publicKey; 143 | this.privateKey = privateKey; 144 | this.defaultCharset = Charset.forName(DEFAULT_ENCODING); 145 | this.algorithm = algorithm; 146 | this.salt = isHex(salt) ? salt : new String(Hex.encode(salt 147 | .getBytes(this.defaultCharset))); 148 | this.gcm = gcm; 149 | } 150 | 151 | @Override 152 | public String getPublicKey() { 153 | return RsaKeyHelper.encodePublicKey((RSAPublicKey) this.publicKey, "application"); 154 | } 155 | 156 | @Override 157 | public String encrypt(String text) { 158 | return new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), 159 | this.defaultCharset); 160 | } 161 | 162 | @Override 163 | public String decrypt(String encryptedText) { 164 | Assert.state(canDecrypt(), "Encryptor is not configured for decryption"); 165 | return new String(decrypt(Base64.getDecoder().decode(encryptedText 166 | .getBytes(this.defaultCharset))), this.charset); 167 | } 168 | 169 | @Override 170 | public byte[] encrypt(byte[] byteArray) { 171 | return encrypt(byteArray, this.publicKey, this.algorithm, this.salt, this.gcm); 172 | } 173 | 174 | @Override 175 | public byte[] decrypt(byte[] encryptedByteArray) { 176 | Assert.state(canDecrypt(), "Encryptor is not configured for decryption"); 177 | return decrypt(encryptedByteArray, this.privateKey, this.algorithm, this.salt, 178 | this.gcm); 179 | } 180 | 181 | private static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg, 182 | String salt, boolean gcm) { 183 | byte[] random = KeyGenerators.secureRandom(16).generateKey(); 184 | BytesEncryptor aes = gcm ? Encryptors.stronger(new String(Hex.encode(random)), 185 | salt) : Encryptors.standard(new String(Hex.encode(random)), salt); 186 | try { 187 | final Cipher cipher = Cipher.getInstance(alg.getJceName()); 188 | cipher.init(Cipher.ENCRYPT_MODE, key); 189 | byte[] secret = cipher.doFinal(random); 190 | ByteArrayOutputStream result = new ByteArrayOutputStream(text.length + 20); 191 | writeInt(result, secret.length); 192 | result.write(secret); 193 | result.write(aes.encrypt(text)); 194 | return result.toByteArray(); 195 | } 196 | catch (RuntimeException e) { 197 | throw e; 198 | } 199 | catch (Exception e) { 200 | throw new IllegalStateException("Cannot encrypt", e); 201 | } 202 | } 203 | 204 | private static void writeInt(ByteArrayOutputStream result, int length) 205 | throws IOException { 206 | byte[] data = new byte[2]; 207 | data[0] = (byte) ((length >> 8) & 0xFF); 208 | data[1] = (byte) (length & 0xFF); 209 | result.write(data); 210 | } 211 | 212 | private static int readInt(ByteArrayInputStream result) throws IOException { 213 | byte[] b = new byte[2]; 214 | result.read(b); 215 | return ((b[0] & 0xFF) << 8) | (b[1] & 0xFF); 216 | } 217 | 218 | private static byte[] decrypt(byte[] text, PrivateKey key, RsaAlgorithm alg, 219 | String salt, boolean gcm) { 220 | ByteArrayInputStream input = new ByteArrayInputStream(text); 221 | ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); 222 | try { 223 | int length = readInt(input); 224 | byte[] random = new byte[length]; 225 | input.read(random); 226 | final Cipher cipher = Cipher.getInstance(alg.getJceName()); 227 | cipher.init(Cipher.DECRYPT_MODE, key); 228 | String secret = new String(Hex.encode(cipher.doFinal(random))); 229 | byte[] buffer = new byte[text.length - random.length - 2]; 230 | input.read(buffer); 231 | BytesEncryptor aes = gcm ? Encryptors.stronger(secret, salt) : Encryptors 232 | .standard(secret, salt); 233 | output.write(aes.decrypt(buffer)); 234 | return output.toByteArray(); 235 | } 236 | catch (RuntimeException e) { 237 | throw e; 238 | } 239 | catch (Exception e) { 240 | throw new IllegalStateException("Cannot decrypt", e); 241 | } 242 | } 243 | 244 | private static boolean isHex(String input) { 245 | try { 246 | Hex.decode(input); 247 | return true; 248 | } 249 | catch (Exception e) { 250 | return false; 251 | } 252 | } 253 | 254 | public boolean canDecrypt() { 255 | return privateKey != null; 256 | } 257 | 258 | } 259 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/security/rsa/crypto/KeyStoreKeyFactoryTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import org.junit.Test; 19 | 20 | import org.springframework.core.io.ClassPathResource; 21 | 22 | import static org.junit.Assert.assertEquals; 23 | import static org.junit.Assert.assertFalse; 24 | import static org.junit.Assert.assertNotEquals; 25 | import static org.junit.Assert.assertTrue; 26 | 27 | /** 28 | * @author Dave Syer 29 | * 30 | */ 31 | public class KeyStoreKeyFactoryTests { 32 | 33 | @Test 34 | public void initializeEncryptorFromKeyStore() throws Exception { 35 | char[] password = "foobar".toCharArray(); 36 | KeyStoreKeyFactory factory = new KeyStoreKeyFactory( 37 | new ClassPathResource("keystore.jks"), password); 38 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("test")); 39 | assertTrue("Should be able to decrypt", encryptor.canDecrypt()); 40 | assertEquals("foo", encryptor.decrypt(encryptor.encrypt("foo"))); 41 | } 42 | 43 | @Test 44 | public void initializeEncryptorFromPkcs12KeyStore() throws Exception { 45 | char[] password = "letmein".toCharArray(); 46 | KeyStoreKeyFactory factory = new KeyStoreKeyFactory( 47 | new ClassPathResource("keystore.pkcs12"), password); 48 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor( 49 | factory.getKeyPair("mytestkey")); 50 | assertTrue("Should be able to decrypt", encryptor.canDecrypt()); 51 | assertEquals("foo", encryptor.decrypt(encryptor.encrypt("foo"))); 52 | } 53 | 54 | @Test 55 | public void initializeEncryptorFromTrustedCertificateInKeyStore() throws Exception { 56 | char[] password = "foobar".toCharArray(); 57 | KeyStoreKeyFactory factory = new KeyStoreKeyFactory( 58 | new ClassPathResource("keystore.jks"), password); 59 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("testcertificate")); 60 | assertFalse("Should not be able to decrypt", encryptor.canDecrypt()); 61 | assertNotEquals("foo", encryptor.encrypt("foo")); 62 | } 63 | 64 | @Test 65 | public void initializeEncryptorFromTrustedCertificateInPkcs12KeyStore() throws Exception { 66 | char[] password = "letmein".toCharArray(); 67 | KeyStoreKeyFactory factory = new KeyStoreKeyFactory( 68 | new ClassPathResource("keystore.pkcs12"), password); 69 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("mytestcertificate")); 70 | assertFalse("Should not be able to decrypt", encryptor.canDecrypt()); 71 | assertNotEquals("foo", encryptor.encrypt("foo")); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/security/rsa/crypto/RsaKeyHelperTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.nio.charset.Charset; 22 | import java.security.KeyPair; 23 | 24 | import org.junit.Test; 25 | import org.springframework.core.io.ClassPathResource; 26 | import org.springframework.util.StreamUtils; 27 | 28 | public class RsaKeyHelperTests { 29 | 30 | @Test 31 | public void parsePrivateKey() throws Exception { 32 | // ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem 33 | String pem = StreamUtils.copyToString(new ClassPathResource("/fake.pem", getClass()).getInputStream(), 34 | Charset.forName("UTF-8")); 35 | KeyPair result = RsaKeyHelper.parseKeyPair(pem); 36 | assertTrue(result.getPrivate().getEncoded().length > 0); 37 | assertEquals("RSA", result.getPrivate().getAlgorithm()); 38 | } 39 | 40 | @Test 41 | public void parseSpaceyKey() throws Exception { 42 | String pem = StreamUtils.copyToString(new ClassPathResource("/spacey.pem", getClass()).getInputStream(), 43 | Charset.forName("UTF-8")); 44 | KeyPair result = RsaKeyHelper.parseKeyPair(pem); 45 | assertTrue(result.getPrivate().getEncoded().length > 0); 46 | assertEquals("RSA", result.getPrivate().getAlgorithm()); 47 | } 48 | 49 | @Test 50 | public void parseBadKey() throws Exception { 51 | // ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem 52 | String pem = StreamUtils.copyToString(new ClassPathResource("/bad.pem", getClass()).getInputStream(), 53 | Charset.forName("UTF-8")); 54 | try { 55 | RsaKeyHelper.parseKeyPair(pem); 56 | throw new IllegalStateException("Expected IllegalArgumentException"); 57 | } catch (IllegalArgumentException e) { 58 | assertTrue(e.getMessage().contains("PEM")); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/security/rsa/crypto/RsaRawEncryptorTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Dave Syer 25 | * 26 | */ 27 | public class RsaRawEncryptorTests { 28 | 29 | private RsaRawEncryptor encryptor = new RsaRawEncryptor(); 30 | 31 | @Before 32 | public void init() { 33 | LONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING; 34 | for (int i = 0; i < 4; i++) { 35 | LONG_STRING = LONG_STRING + LONG_STRING; 36 | } 37 | } 38 | 39 | @Test 40 | public void roundTrip() { 41 | assertEquals("encryptor", 42 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 43 | } 44 | 45 | @Test 46 | public void roundTripOeap() { 47 | this.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP); 48 | assertEquals("encryptor", 49 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 50 | } 51 | 52 | @Test 53 | public void roundTripLongString() { 54 | assertEquals(LONG_STRING, 55 | this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))); 56 | } 57 | 58 | @Test 59 | public void roundTripLongStringOeap() { 60 | this.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP); 61 | assertEquals(LONG_STRING, 62 | this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))); 63 | } 64 | 65 | @Test 66 | public void roundTrip2048Key() { 67 | String pemData = "-----BEGIN RSA PRIVATE KEY-----" + 68 | "MIIEpQIBAAKCAQEA5KHEkCudAHCKIUHKyW6Z8dMyQsKrLbpDe0wDzx9MBARcOoS9" + 69 | "ZUjzXwK6p/0RM6aCp+b9kkr37QKQ9K/Am13sr0z8Mkn1Q2cvXiL5gbnY1nYGk8/m" + 70 | "CBX3QEhH2UII4yJsDVx1xmcSorZaWmeNKor7Zl3SZaQpWTvlkMgQKwY8DZL6PPxt" + 71 | "JRPeKmuUY6B59u5okh1G6Y9OnT2dVxAkqT8WgLHu6StxBmueJ272x2sUWUzoDhnP" + 72 | "7JRqa7h7t6fml3o3Op1iCywCOFzCIcK6G/oG/WZ7tbBYkwQdDjn/9VMdKkkPufwq" + 73 | "zt4S75NJygXDwDnNPiTVoaOwrRrL8ahgw6bFCQIDAQABAoIBAECIMHUI+l2fZj2Q" + 74 | "1m4Ym7cYB320eKCFjHqGsCSMDuarXGTgBp1KA/dzS8ASvAI6I3LEzhm2s1fge420" + 75 | "9cZksmOgdSa0nVeTDlmhwY8OJ9gQpDagXas2l/066Zy2+M8zbhAvYsbHXQk0MziF" + 76 | "NeEmLWNtY+9wcINRVrCQ549dSSIDK6UX21oU6d1mrlnF5/bbbdDIM3dKok355jwx" + 77 | "0HFY0tJIs1zArsBVoz3Ccu1MQEfnxEFM1LLPi5rE6cuHIOBinbD1OQ2R/HM2aukG" + 78 | "Rk2m6F3wAieJ7zpt5yaHuuIedn8p8m2NVulXAjgkY2oQl3GGiDH/H7eZlrvQRg6E" + 79 | "D8Bq+ykCgYEA+AfPXVeeVg3Qu0KsNrACek/o92BMY9g3GyPVGULGvq9seoNB86hj" + 80 | "nXasqngBfTlOfJFiahoEzRBB9hIyo1zMw4x99pR8nGxhR3aU+v8EGftMABGHWsB9" + 81 | "Jxj4YQH4fhi57iBa72QmNPbu/1o7y3SEe68E5PJ8KY3jc4xos8Vl658CgYEA6/pk" + 82 | "t6WZII+9lpxQfePQDIlBWAphiQceh995bGXfDmX3vOVmPozix9/fUtF1TeKS/ypw" + 83 | "u++Qmvj5oMsBVrjCyoOYfHKE2vGrLoEzkX/sPO65IsV00geZZoyCEKEE3USJfY46" + 84 | "u0hs61oP8HJyLhLiYiGcFTzZ4nEvvEbiM4E/DlcCgYEA6S0OecZhiK08SpAHrvIR" + 85 | "okN11PqnVkZyqAUr1a+9gI8TAKpdWmA4JlTnRuvDGqLBcsKLLwx+7voVyOyaxpH7" + 86 | "vutZkHNQIw6Q9co5jS4qAPMLJBVWlq7X+eWzvB9KKeG9Cm1IkD4q3Sg4z79Y75D+" + 87 | "6/hCNarxp29JIdwior81bikCgYEApp1P+b7pxGzZPvs1df2hCwjqY0BJJ5goPWVT" + 88 | "dW7kNGVYqz4JmAafpOJz6yTLP2fHxHRxzrBSmKlMj/RmCJZBqv2Jb+zn0zMpW5eM" + 89 | "EqKQ6WDgxSVH23fUHuz8dMNMDPL0ZPtEirGTfgVEFdCov9FDmGgErZYefVzPiI8/" + 90 | "7X/HRtcCgYEApQ2YS+0DLPqaM0cC6/6hDr/jmHLFhHaV6DZR7M9HHDnMN2uMlOEa" + 91 | "RYvXRMBjyQ7LQkwOj6K5k8MVrsDDM5dbekTBgcJMHfM9uViDkB0VPYULORmDJ20N" + 92 | "MLowIAiSon2B2/isatY80YtFq+bRyvPOzjGvinHN3MU1GH/gFuS0fiw=" + 93 | "-----END RSA PRIVATE KEY-----"; 94 | RsaRawEncryptor encryptor_2048 = new RsaRawEncryptor(pemData); 95 | assertEquals("encryptor", 96 | encryptor_2048.decrypt(encryptor_2048.encrypt("encryptor"))); 97 | } 98 | 99 | @Test 100 | public void roundTrip4096Key() { 101 | String pemData = "-----BEGIN RSA PRIVATE KEY-----" + 102 | "MIIJKAIBAAKCAgEAw/OIcO1pv8t/lhXwzc+CqCqAE8+2+BTWd6fHy8P2oGKZK0s3" + 103 | "jxPWdZEbp1soGZobCIjEIuYuuPeinrTFOxtnf/JVfmzGnixRjWzQK0UiM/4z8GW6" + 104 | "7+dzB0+QZlU+PGCL6xra4d3+5EsPQwTDjPJ4OhcA66hWACd3UJpvE2C14YdFkCP/" + 105 | "CUxubz1l+8rFwEtMcw2bVUL/Mt+Sx1CHPFer17VK/sT4urwNG7y9R8WWvNQXgEwg" + 106 | "0im+iJ0zf1u0SdUVj+Q1LwgNRoIx4vec2xAJ6xdqSx3Y3g2twWqUXUBb5K09ajIW" + 107 | "Vuko5kWJVyx1x8LazU+0wQRLVJRYAiUOPLg7PdPAJWaAWmagnkAvl5bqCKi6sIc8" + 108 | "+vKyrPx4VJH5KLsHx8020Wgch/LfHl/vvoHE7Oa81hnyMVsApvNCJdFbiMJ6r2z/" + 109 | "eHqzjY8lzBQHNxh1XJys5teTJsi6N06gCc+OQRyw1FQ8KLgFlLPHNamfMnP5Ju0d" + 110 | "Jv8GzQiMFjudjEYhkh2GPmRus1VYWDwDWhXwp28koWAanfih+Ujc2ZqNUS23hGWz" + 111 | "KbCxRaAwSLqn3vkoYBeDyWWs1r0HnB6gACFaZIk38aiGyg7GjF0286Aq7USqNwKu" + 112 | "Izm4kzIPFrHIbywKq7804J7wXUlaAgf0pNSndMD5OnwudzD+JHLTuOGFNdUCAwEA" + 113 | "AQKCAgBYh2mIY6rYTS9adpUx1uPX6EOvL7QhhwCSVMoupF2Dfqhm5/e0+6hzu1h8" + 114 | "FvIaBwbZpzi977MCPFdLTq6hErODGdBIawqdIbbCp3uxYO2gAeQjY0K+6pmMnwTF" + 115 | "RxP0IUZ1tM9ZJnvnVoYRqFBVGKL607PFxGr+bNY6I1u1rIbf2sax5aFu6Qon1dyC" + 116 | "ks0fIKXsgSRBtCAqMtpUlGxU9eMcdLrqOcGKVDWz52S4zWtZ6pSnkT1u1g9QF33R" + 117 | "t3PPu6afOOJSWlftGBtDyM0kJ63jedO7FkQJprJu5SEctFwQB7jshq6TG4ov5xCy" + 118 | "wtJ/quhBxBYM8ky6bL8KUQWKp02Tyfq0Fo+iwuLxM4N6LxVPFZ6R6jwvazm+ka4S" + 119 | "sZAW/hnH3FdJEAyFcxzhelLdLUrjwrsWjmJBk0pMP5cEleYR8PQh2sHM8ZOX1T5f" + 120 | "4zfyR66+tl1O81T7anbma8l1Wm/QSNZz+8QAM1iNuV+uLsWvmxLAc7NRgjDmiAMn" + 121 | "8VhfUtl0ooOZYkDexqSNaWvIQG+S8Pl28gNxVXkXrXqBGPJn2ptROEJ1/AN1h4cv" + 122 | "2CktVylRFpEI/hxXvKMaAu/tXtvoakvaTA8msl8Otrldsy3EGhgHrDTYIJUg/rRT" + 123 | "TlbRkN/ycaOhA0d4HAewOGul3ss+EtBz+SQBzaWm2Inr8XOJoQKCAQEA4LwW7eGm" + 124 | "MOYspFUbn2tMlnJAng9HKK42o2m6ShYAaQAoLX7LIkQYVS++9CiGCPpoSlwIJWE3" + 125 | "N/qGx0i7REDm+wNu0/4acaMFI+qYtvjKiWwtMOBH3bw1C4/Isc60tFPkI7FEFCiF" + 126 | "SiW3c+Z8B0/IRMb/YF5tZeuWUlAl7PQJ1rMcPUE4O4LXM4BG29hghVGGnp39YsOY" + 127 | "b/6oBApTgdxCaSZhmhDwTMu97n75CK0xzA2vDtHn2Gu3zf4j6bsNot6/7wRtQBMg" + 128 | "1e3kXuwGUZ08QZ7OqATUIZdCeK1PfxypontVh+0LeNjiDU8pW3Q8IMlDT96Fd5U+" + 129 | "BgtjfHmwHXeBmQKCAQEA3zZS619O/IUoWN3rWT4hUSJE3S+FXXcaBaJ7H6r897cl" + 130 | "ju+HSS2CLp/C9ftcQ9ef+pG2arLRZpONd5KhfRyjo0pNp3SwxklnIhNS9abBBCnN" + 131 | "ojeYcVHOcSfmWGlUCQAvv5LeBPSS02pbCE5t/qadglvgKhHqSb2u+FgkdKrV0Mme" + 132 | "sbVy+tyd4F1oBIS0wg1p3mHKvKfb4MEnUDvIvG8rCBUMvAWQmTiuyqFUiuqSwEMy" + 133 | "LANFFV/ZoJ5194ruTXdelcoZjXhd128JJFNp6Jh4eg5OWoBS7e08QHbvUYBppDYO" + 134 | "Iz0N1TipVK9uCqHHtbwIqqxyPVev3QJUYkpl5/tznQKCAQB9izV38F2J5Zu8tbq3" + 135 | "pRZk2TCV280RwbjOMysZZg8WmTrYp4NNAiNhu0l+VgEClPibyavXTeauA+s0+sF6" + 136 | "kJM4WKOaE9Kr9rjRZqWnWXazrFXWfwRGr3QmoE0qX2H9dvv0oHt6k2RalpVUTsas" + 137 | "wvoKyewx5q5QiHoyQ4ncRDwWz3oQEhYa0K3tnFR5TfglofSFOZcqjD/lGKq9jxM1" + 138 | "cVk8Km/NxHapQAw7Zn0yRqaR6ncH3WUaNpq4nadsU817Vdp86MkrSURHnhy8lje1" + 139 | "chQOSGwD2qaymTBN/+twBBATr7iJNXf6K5akfruI1nccjbJntNR0iE/cypHqIISt" + 140 | "AWzJAoIBAFDV5ZWkAIDm4EO+qpq5K2usk2/e49eDaIMd4qUHUXGMfCeVi1LvDjRA" + 141 | "W2Sl0TYogqFF3+AoPjl9uj/RdHZQxto98H1yfwpwTs9CXErmRwRw9y2GIMj5LWBB" + 142 | "aOQf0PUpgiFI2OrGf93cqHcLoD4WrPgmubnCnyxxa0o48Yrmy2Q/gB8vbSJ4fxxf" + 143 | "92mbfbLBFNQaakeEKtbsXIZsADhtshHNPb1h7onuwy5S2sEsTlUegK77yCsDeVb3" + 144 | "zBUH1WFsl257sGFRc/qvFYp4QuSfQxJA2BNiYaYUwjs+V1EWxitYACq206miSYCH" + 145 | "v7xN9ntUS3cz2HNqrB/H1jN6aglnQOkCggEBAJb5FYvQCvw5PJM44nR6/U1cSlr4" + 146 | "lRWcuFp7Xv5kWxSwM5115qic14fByh7DbaTHxxoPEhEA4aJ2QcDa7YWvabVc/VEV" + 147 | "VacAAdg44+WSw6FNni18K53oOKAONgzSQlYUm/jgENIXi+5L0Yq7qAbnldiC6jXr" + 148 | "yqbEwZjmpt8xsBLnl37k/LSLG1GUaYV8AK3s9UDs9/jv5RUrV96jiXed+7pYrjmj" + 149 | "o1yJ4WAqouYHmOQCI3SeFCLT8GCdQ+uE74G5q+Yte6YT9jqSiGDjrst0bjtN640v" + 150 | "YKRG3XK4AE9i4Oinnv/Ua95ql0syphn+CPW2ksmGon5/0mbK5qYsg47Hdls=" + 151 | "-----END RSA PRIVATE KEY-----"; 152 | RsaRawEncryptor encryptor_4096 = new RsaRawEncryptor(pemData); 153 | assertEquals("encryptor", 154 | encryptor_4096.decrypt(encryptor_4096.encrypt("encryptor"))); 155 | } 156 | 157 | private static final String SHORT_STRING = "Bacon ipsum dolor sit amet tail pork loin pork chop filet mignon flank fatback tenderloin boudin shankle corned beef t-bone short ribs. Meatball capicola ball tip short loin beef ribs shoulder, kielbasa pork chop meatloaf biltong porchetta bresaola t-bone spare ribs. Andouille t-bone sausage ground round frankfurter venison. Ground round meatball chicken ribeye doner tongue porchetta."; 158 | private static String LONG_STRING; 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/security/rsa/crypto/RsaSecretEncryptorTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.rsa.crypto; 17 | 18 | import java.security.PublicKey; 19 | import java.security.interfaces.RSAPublicKey; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertFalse; 26 | 27 | /** 28 | * @author Dave Syer 29 | * 30 | */ 31 | public class RsaSecretEncryptorTests { 32 | 33 | private RsaSecretEncryptor encryptor = new RsaSecretEncryptor(); 34 | 35 | @Before 36 | public void init() { 37 | LONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING; 38 | for (int i = 0; i < 4; i++) { 39 | LONG_STRING = LONG_STRING + LONG_STRING; 40 | } 41 | } 42 | 43 | @Test 44 | public void roundTripKey() { 45 | PublicKey key = RsaKeyHelper.generateKeyPair().getPublic(); 46 | String encoded = RsaKeyHelper.encodePublicKey((RSAPublicKey) key, "application"); 47 | assertEquals(key, RsaKeyHelper.parsePublicKey(encoded)); 48 | } 49 | 50 | @Test 51 | public void roundTrip() { 52 | assertEquals("encryptor", 53 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 54 | } 55 | 56 | @Test 57 | public void roundTripWithSalt() { 58 | this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "somesalt"); 59 | assertEquals("encryptor", 60 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 61 | } 62 | 63 | @Test 64 | public void roundTripWithHexSalt() { 65 | this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "beefea"); 66 | assertEquals("encryptor", 67 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 68 | } 69 | 70 | @Test 71 | public void roundTripWithLongSalt() { 72 | this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "somesaltsomesaltsomesaltsomesaltsomesalt"); 73 | assertEquals("encryptor", 74 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 75 | } 76 | 77 | @Test 78 | public void roundTripOaep() { 79 | this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP); 80 | assertEquals("encryptor", 81 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 82 | } 83 | 84 | @Test 85 | public void roundTripOaepGcm() { 86 | this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, true); 87 | assertEquals("encryptor", 88 | this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))); 89 | } 90 | 91 | @Test(expected=IllegalStateException.class) 92 | public void roundTripWithMixedAlgorithm() { 93 | RsaSecretEncryptor oaep = new RsaSecretEncryptor(RsaAlgorithm.OAEP); 94 | assertEquals("encryptor", oaep.decrypt(this.encryptor.encrypt("encryptor"))); 95 | } 96 | 97 | @Test(expected=IllegalStateException.class) 98 | public void roundTripWithMixedSalt() { 99 | RsaSecretEncryptor other = new RsaSecretEncryptor(this.encryptor.getPublicKey(), RsaAlgorithm.DEFAULT, "salt"); 100 | assertEquals("encryptor", this.encryptor.decrypt(other.encrypt("encryptor"))); 101 | } 102 | 103 | @Test 104 | public void roundTripWithPublicKeyEncryption() { 105 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor( 106 | this.encryptor.getPublicKey()); 107 | RsaSecretEncryptor decryptor = this.encryptor; 108 | assertEquals("encryptor", decryptor.decrypt(encryptor.encrypt("encryptor"))); 109 | } 110 | 111 | @Test(expected = IllegalStateException.class) 112 | public void publicKeyCannotDecrypt() { 113 | RsaSecretEncryptor encryptor = new RsaSecretEncryptor( 114 | this.encryptor.getPublicKey()); 115 | assertFalse("Encryptor schould not be able to decrypt", encryptor.canDecrypt()); 116 | assertEquals("encryptor", encryptor.decrypt(encryptor.encrypt("encryptor"))); 117 | } 118 | 119 | @Test 120 | public void roundTripLongString() { 121 | assertEquals(LONG_STRING, 122 | this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))); 123 | } 124 | 125 | private static final String SHORT_STRING = "Bacon ipsum dolor sit amet tail pork loin pork chop filet mignon flank fatback tenderloin boudin shankle corned beef t-bone short ribs. Meatball capicola ball tip short loin beef ribs shoulder, kielbasa pork chop meatloaf biltong porchetta bresaola t-bone spare ribs. Andouille t-bone sausage ground round frankfurter venison. Ground round meatball chicken ribeye doner tongue porchetta."; 126 | private static String LONG_STRING; 127 | } 128 | -------------------------------------------------------------------------------- /src/test/resources/bad.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 -------------------------------------------------------------------------------- /src/test/resources/fake.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQDMWnfaQ0yLFXelprq2S8UurnaGvxFNUdbmTyJeycem5vGLycEY 3 | T4KcdVCTU5491cjbk5GcHjoj2efRSO0y0aXIlUJpLofDdML/SuGLZWp/GbEv978M 4 | pZIztK8iaIm7D/D7by8aws1RJyD9T+lZDAGY7eFfMp0EQyHOcEL0NGFLuwIDAQAB 5 | AoGAWwC6uO8ZaiKwOouqQD4z3FsDG3SA/v7ABaYd9zpCd9gGnyrEm8/kqUoxDLrD 6 | EGRg4y+vO2fWmlqSuoeQYf4spf+vi2di+mGIb6nGe7TpMLPa7lFLOSQHZRx5M5H6 7 | JDhfhAHlKmF9gLGvDHbpyErzn5YXjcu0PoFiNC1y445D8iECQQDvJzkGbJ9l9vb0 8 | oRyGXRDpddUcVMECLLB9NKmTl/zKy/qVPD+zYNoi87ePBJFbgmAXRjhhTk2uSBRP 9 | NtVaMoXLAkEA2r+ugzjsLZQIYz/9gxdzdbKWDgpSPbhKCR4bOmrDgJMcOVjtwW+n 10 | +liaX6zwI0QEgCAWLzCbbYDmj3kJrRwT0QJAaowg/dm7EmR7FfYJjVs9Q6X5skuY 11 | Se27G60wt88JExjZpU9YWgSWaugGKbOxRwHI6dWhHMkUFseKNNiLKUpFDQJALIGP 12 | ahdsxiE2S6s7Uy60SSAas6SZ8wDJ320GsS4DtOc5eNmFFjQ3gxH/5rNy8FnoaIEe 13 | wl8rYG43er1voI7z4QJAB4qaqBo7eeiRgnUVIccaSZkNIMSrZ9QUjVFRgfLwAXDO 14 | Ae+t6V+eB0oaIXczA+BLj3Oe6D3iHRGHrxGlcvDdHw== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /src/test/resources/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/spring-security-rsa/8c6649bf3400c6b9e7f1db593cb4b9239aab4f9e/src/test/resources/keystore.jks -------------------------------------------------------------------------------- /src/test/resources/keystore.pkcs12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/spring-security-rsa/8c6649bf3400c6b9e7f1db593cb4b9239aab4f9e/src/test/resources/keystore.pkcs12 -------------------------------------------------------------------------------- /src/test/resources/spacey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 3 | 4YbrwfKwjoTjk1M6gLQpOA4Blocx6zN5OnICnVGlVM9xymWxTxxCfc2tE2Fai9I1wchULCChhwm/UU5ZNi3KpXinlyamSYw+lMQkZ8gTXCgOEvs2j9E1quF4pvy1BZKvbD8tUnUQlyiKRnI6gOxQL8B6OAYPRdaa9FVNmrs1B4eDPG918L2f1pT090P1n+tw 4 | iejNgQvtSD78/A88qt89OhzscsufALTrBjycn89kkfBd0zbVLF0W6+ZVLZrf97/y 5 | LCoGSCcZL9LFPNvNqxOnleviDco7aOs4stQ9jQIDAQABAoIBAQC1TbthyN0YUe+T 6 | 7dIDAbbZaVrU00biOtXgzjMADmTprP7Hf18UpIIIKfzfWw6FUD+gc1t4oe5pogE9 7 | UwGMXUmOORxu2pMYTb5vT9CEdexYnsAZsCo8PdD9GYSNrmquQef2MFpEqYQmHrdC 8 | KWpaXn2i1ak+iCRPUGp4YwHpynZVxfE8z/AIsPn6NPDh6SnCXb1rTgQe2UCfXm93 9 | UJe5F/OR2kQi5KFO+dxLmCOBCwr6SGCLH+VotGpuxCVRUd9sJ/d4QpDZEgjuf7Ug 10 | eQHfgMDS/tc09B9rl0dwKnEa31kcQ9X9KLkKP+w0Pqhh0Emny20eg9jS6XNayg61 11 | p/LQtW9BAoGBAO5veKMIcXfZmuh11WIIhdhLKkNtYyt5NDmrV8/IVScLFvjB0ftt 12 | 8PAtXo/ekOHkyITyIumQ9l4VCvacNw7DyV9FYk4WvrvVYOCL8aZi+O5+12NT67eO 13 | Rr/voGlRoV05X7+inc90qbbYJ8lRmLSqvzmsm98mkuhw/FKGRhVZIfAJAoGBAM5R 14 | I5vK6cJxOwXQOEGOd5/8B9JMFXyuendXo/N2/NxSQsbx4pc3v2rv/eGJYaY7Nx/y 15 | 2M/vdWYkpG59PAS3k2TrCA/0SGmyVqY+c8BomKisU5VaBlIPfGuec9tDPgWCp8Ur 16 | 3Jjt/2sVoa0vMkqymUqMb9HyH9tdI9oyh7EOOrplAoGAR6DlNNUMgVy11K/Rcqns 17 | y5WJFMh/ykeXENwQfTNJoXkLZZ+UXVwhzYVTqxTJoZMBSi8TnecWnBzmNj+nqp/W 18 | lvBZH+xlUDhB6jMgXUPOVJd2TTigz3vGdVKfdgQ33bGmugM4NWJuuacmDKyem2fQ 19 | GptoGBmWeI24v3HnC/LC50ECgYAz0iN8hRnz0db+Xc9TgAJB997LDnszJuvxv9yZ 20 | UWCvwiWtrKG6U7FLnd4J4STayPLOnoOgrsexETEP43rIwIdQCMysnTH3AmlLNlKC 21 | mIMHksknsUX3JJaevVziTOBuJ+QV3S96ZgUKk5NZWYprQrLIC8AmXodr5NgVfS2h 22 | 5i4QFQKBgFfbYHiMw5AAUQrBNkrAjLd1wIaO/6qS3w4OsCWKowhfaJLEXAbIRV7s 23 | vAtgtlCovdasVj4RRLXFf+73naVTQjBZI+3jWHHyFk3+Zy86mQCSGv9WuDVV1IhS 24 | h8InTVvK8wgdgX7qiw3pvU0roqNW4/j4j8OqJO3Zt4KO2iX8htsO 25 | -----END RSA PRIVATE KEY----- 26 | --------------------------------------------------------------------------------