17 changed files with 1320 additions and 1 deletions
@ -0,0 +1,96 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
import java.security.KeyFactory; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.KeyStore; |
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.cert.Certificate; |
||||||
|
import java.security.interfaces.RSAPrivateCrtKey; |
||||||
|
import java.security.spec.RSAPublicKeySpec; |
||||||
|
|
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* @author Tim Ysewyn |
||||||
|
* @since 6.3 |
||||||
|
*/ |
||||||
|
public class KeyStoreKeyFactory { |
||||||
|
|
||||||
|
private final Resource resource; |
||||||
|
|
||||||
|
private final char[] password; |
||||||
|
|
||||||
|
private KeyStore store; |
||||||
|
|
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
private final String type; |
||||||
|
|
||||||
|
public KeyStoreKeyFactory(Resource resource, char[] password) { |
||||||
|
this(resource, password, type(resource)); |
||||||
|
} |
||||||
|
|
||||||
|
private static String type(Resource resource) { |
||||||
|
String ext = StringUtils.getFilenameExtension(resource.getFilename()); |
||||||
|
return (ext != null) ? ext : "jks"; |
||||||
|
} |
||||||
|
|
||||||
|
public KeyStoreKeyFactory(Resource resource, char[] password, String type) { |
||||||
|
this.resource = resource; |
||||||
|
this.password = password; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public KeyPair getKeyPair(String alias) { |
||||||
|
return getKeyPair(alias, this.password); |
||||||
|
} |
||||||
|
|
||||||
|
public KeyPair getKeyPair(String alias, char[] password) { |
||||||
|
try { |
||||||
|
synchronized (this.lock) { |
||||||
|
if (this.store == null) { |
||||||
|
synchronized (this.lock) { |
||||||
|
this.store = KeyStore.getInstance(this.type); |
||||||
|
try (InputStream stream = this.resource.getInputStream()) { |
||||||
|
this.store.load(stream, this.password); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
RSAPrivateCrtKey key = (RSAPrivateCrtKey) this.store.getKey(alias, password); |
||||||
|
Certificate certificate = this.store.getCertificate(alias); |
||||||
|
PublicKey publicKey = null; |
||||||
|
if (certificate != null) { |
||||||
|
publicKey = certificate.getPublicKey(); |
||||||
|
} |
||||||
|
else if (key != null) { |
||||||
|
RSAPublicKeySpec spec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()); |
||||||
|
publicKey = KeyFactory.getInstance("RSA").generatePublic(spec); |
||||||
|
} |
||||||
|
return new KeyPair(publicKey, key); |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new IllegalStateException("Cannot load keys from store: " + this.resource, ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* @since 6.3 |
||||||
|
*/ |
||||||
|
public enum RsaAlgorithm { |
||||||
|
|
||||||
|
DEFAULT("RSA", 117), OAEP("RSA/ECB/OAEPPadding", 86); |
||||||
|
|
||||||
|
private final String name; |
||||||
|
|
||||||
|
private final int maxLength; |
||||||
|
|
||||||
|
RsaAlgorithm(String name, int maxLength) { |
||||||
|
this.name = name; |
||||||
|
this.maxLength = maxLength; |
||||||
|
} |
||||||
|
|
||||||
|
public String getJceName() { |
||||||
|
return this.name; |
||||||
|
} |
||||||
|
|
||||||
|
public int getMaxLength() { |
||||||
|
return this.maxLength; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,284 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.math.BigInteger; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.nio.CharBuffer; |
||||||
|
import java.nio.charset.CharacterCodingException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.security.KeyFactory; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.KeyPairGenerator; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
import java.security.PrivateKey; |
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
import java.security.spec.InvalidKeySpecException; |
||||||
|
import java.security.spec.KeySpec; |
||||||
|
import java.security.spec.RSAPrivateCrtKeySpec; |
||||||
|
import java.security.spec.RSAPublicKeySpec; |
||||||
|
import java.security.spec.X509EncodedKeySpec; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Base64; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1Sequence; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads RSA key pairs using BC provider classes but without the need to specify a crypto |
||||||
|
* provider or have BC added as one. |
||||||
|
* |
||||||
|
* @author Luke Taylor |
||||||
|
* @author Dave Syer |
||||||
|
*/ |
||||||
|
final class RsaKeyHelper { |
||||||
|
|
||||||
|
private static final Charset UTF8 = StandardCharsets.UTF_8; |
||||||
|
|
||||||
|
private static final String BEGIN = "-----BEGIN"; |
||||||
|
|
||||||
|
private static final Pattern PEM_DATA = Pattern.compile(".*-----BEGIN (.*)-----(.*)-----END (.*)-----", |
||||||
|
Pattern.DOTALL); |
||||||
|
|
||||||
|
private static final byte[] PREFIX = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' }; |
||||||
|
|
||||||
|
private RsaKeyHelper() { |
||||||
|
} |
||||||
|
|
||||||
|
static KeyPair parseKeyPair(String pemData) { |
||||||
|
Matcher m = PEM_DATA.matcher(pemData.replaceAll("\n *", "").trim()); |
||||||
|
|
||||||
|
if (!m.matches()) { |
||||||
|
try { |
||||||
|
RSAPublicKey publicValue = extractPublicKey(pemData); |
||||||
|
if (publicValue != null) { |
||||||
|
return new KeyPair(publicValue, null); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
// Ignore
|
||||||
|
} |
||||||
|
throw new IllegalArgumentException("String is not PEM encoded data, nor a public key encoded for ssh"); |
||||||
|
} |
||||||
|
|
||||||
|
String type = m.group(1); |
||||||
|
final byte[] content = base64Decode(m.group(2)); |
||||||
|
|
||||||
|
PublicKey publicKey; |
||||||
|
PrivateKey privateKey = null; |
||||||
|
|
||||||
|
try { |
||||||
|
KeyFactory fact = KeyFactory.getInstance("RSA"); |
||||||
|
switch (type) { |
||||||
|
case "RSA PRIVATE KEY" -> { |
||||||
|
ASN1Sequence seq = ASN1Sequence.getInstance(content); |
||||||
|
if (seq.size() != 9) { |
||||||
|
throw new IllegalArgumentException("Invalid RSA Private Key ASN1 sequence."); |
||||||
|
} |
||||||
|
org.bouncycastle.asn1.pkcs.RSAPrivateKey key = org.bouncycastle.asn1.pkcs.RSAPrivateKey |
||||||
|
.getInstance(seq); |
||||||
|
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()); |
||||||
|
RSAPrivateCrtKeySpec privSpec = new RSAPrivateCrtKeySpec(key.getModulus(), key.getPublicExponent(), |
||||||
|
key.getPrivateExponent(), key.getPrime1(), key.getPrime2(), key.getExponent1(), |
||||||
|
key.getExponent2(), key.getCoefficient()); |
||||||
|
publicKey = fact.generatePublic(pubSpec); |
||||||
|
privateKey = fact.generatePrivate(privSpec); |
||||||
|
} |
||||||
|
case "PUBLIC KEY" -> { |
||||||
|
KeySpec keySpec = new X509EncodedKeySpec(content); |
||||||
|
publicKey = fact.generatePublic(keySpec); |
||||||
|
} |
||||||
|
case "RSA PUBLIC KEY" -> { |
||||||
|
ASN1Sequence seq = ASN1Sequence.getInstance(content); |
||||||
|
org.bouncycastle.asn1.pkcs.RSAPublicKey key = org.bouncycastle.asn1.pkcs.RSAPublicKey |
||||||
|
.getInstance(seq); |
||||||
|
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()); |
||||||
|
publicKey = fact.generatePublic(pubSpec); |
||||||
|
} |
||||||
|
default -> throw new IllegalArgumentException(type + " is not a supported format"); |
||||||
|
} |
||||||
|
|
||||||
|
return new KeyPair(publicKey, privateKey); |
||||||
|
} |
||||||
|
catch (InvalidKeySpecException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
catch (NoSuchAlgorithmException ex) { |
||||||
|
throw new IllegalStateException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] base64Decode(String string) { |
||||||
|
try { |
||||||
|
ByteBuffer bytes = UTF8.newEncoder().encode(CharBuffer.wrap(string)); |
||||||
|
byte[] bytesCopy = new byte[bytes.limit()]; |
||||||
|
System.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit()); |
||||||
|
return Base64.getDecoder().decode(bytesCopy); |
||||||
|
} |
||||||
|
catch (CharacterCodingException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static String base64Encode(byte[] bytes) { |
||||||
|
try { |
||||||
|
return UTF8.newDecoder().decode(ByteBuffer.wrap(Base64.getEncoder().encode(bytes))).toString(); |
||||||
|
} |
||||||
|
catch (CharacterCodingException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static KeyPair generateKeyPair() { |
||||||
|
try { |
||||||
|
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); |
||||||
|
keyGen.initialize(1024); |
||||||
|
return keyGen.generateKeyPair(); |
||||||
|
} |
||||||
|
catch (NoSuchAlgorithmException ex) { |
||||||
|
throw new IllegalStateException(ex); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private static final Pattern SSH_PUB_KEY = Pattern.compile("ssh-(rsa|dsa) ([A-Za-z0-9/+]+=*) (.*)"); |
||||||
|
|
||||||
|
private static RSAPublicKey extractPublicKey(String key) { |
||||||
|
|
||||||
|
Matcher m = SSH_PUB_KEY.matcher(key); |
||||||
|
|
||||||
|
if (m.matches()) { |
||||||
|
String alg = m.group(1); |
||||||
|
String encKey = m.group(2); |
||||||
|
// String id = m.group(3);
|
||||||
|
|
||||||
|
if (!"rsa".equalsIgnoreCase(alg)) { |
||||||
|
throw new IllegalArgumentException("Only RSA is currently supported, but algorithm was " + alg); |
||||||
|
} |
||||||
|
|
||||||
|
return parseSSHPublicKey(encKey); |
||||||
|
} |
||||||
|
else if (!key.startsWith(BEGIN)) { |
||||||
|
// Assume it's the plain Base64 encoded ssh key without the
|
||||||
|
// "ssh-rsa" at the start
|
||||||
|
return parseSSHPublicKey(key); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
static RSAPublicKey parsePublicKey(String key) { |
||||||
|
|
||||||
|
RSAPublicKey publicKey = extractPublicKey(key); |
||||||
|
|
||||||
|
if (publicKey != null) { |
||||||
|
return publicKey; |
||||||
|
} |
||||||
|
|
||||||
|
KeyPair kp = parseKeyPair(key); |
||||||
|
|
||||||
|
if (kp.getPublic() == null) { |
||||||
|
throw new IllegalArgumentException("Key data does not contain a public key"); |
||||||
|
} |
||||||
|
|
||||||
|
return (RSAPublicKey) kp.getPublic(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
static String encodePublicKey(RSAPublicKey key, String id) { |
||||||
|
StringWriter output = new StringWriter(); |
||||||
|
output.append("ssh-rsa "); |
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
||||||
|
try { |
||||||
|
stream.write(PREFIX); |
||||||
|
writeBigInteger(stream, key.getPublicExponent()); |
||||||
|
writeBigInteger(stream, key.getModulus()); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new IllegalStateException("Cannot encode key", ex); |
||||||
|
} |
||||||
|
output.append(base64Encode(stream.toByteArray())); |
||||||
|
output.append(" " + id); |
||||||
|
return output.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private static RSAPublicKey parseSSHPublicKey(String encKey) { |
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(base64Decode(encKey)); |
||||||
|
|
||||||
|
byte[] prefix = new byte[11]; |
||||||
|
|
||||||
|
try { |
||||||
|
if (in.read(prefix) != 11 || !Arrays.equals(PREFIX, prefix)) { |
||||||
|
throw new IllegalArgumentException("SSH key prefix not found"); |
||||||
|
} |
||||||
|
|
||||||
|
BigInteger e = new BigInteger(readBigInteger(in)); |
||||||
|
BigInteger n = new BigInteger(readBigInteger(in)); |
||||||
|
|
||||||
|
return createPublicKey(n, e); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static RSAPublicKey createPublicKey(BigInteger n, BigInteger e) { |
||||||
|
try { |
||||||
|
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(n, e)); |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void writeBigInteger(ByteArrayOutputStream stream, BigInteger num) throws IOException { |
||||||
|
int length = num.toByteArray().length; |
||||||
|
byte[] data = new byte[4]; |
||||||
|
data[0] = (byte) ((length >> 24) & 0xFF); |
||||||
|
data[1] = (byte) ((length >> 16) & 0xFF); |
||||||
|
data[2] = (byte) ((length >> 8) & 0xFF); |
||||||
|
data[3] = (byte) (length & 0xFF); |
||||||
|
stream.write(data); |
||||||
|
stream.write(num.toByteArray()); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] readBigInteger(ByteArrayInputStream in) throws IOException { |
||||||
|
byte[] b = new byte[4]; |
||||||
|
|
||||||
|
if (in.read(b) != 4) { |
||||||
|
throw new IOException("Expected length data as 4 bytes"); |
||||||
|
} |
||||||
|
|
||||||
|
int l = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF); |
||||||
|
|
||||||
|
b = new byte[l]; |
||||||
|
|
||||||
|
if (in.read(b) != l) { |
||||||
|
throw new IOException("Expected " + l + " key bytes"); |
||||||
|
} |
||||||
|
|
||||||
|
return b; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* @since 6.3 |
||||||
|
*/ |
||||||
|
public interface RsaKeyHolder { |
||||||
|
|
||||||
|
String getPublicKey(); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,168 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.PrivateKey; |
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.interfaces.RSAKey; |
||||||
|
import java.security.interfaces.RSAPrivateKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
import java.util.Base64; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* @since 6.3 |
||||||
|
*/ |
||||||
|
public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder { |
||||||
|
|
||||||
|
private static final String DEFAULT_ENCODING = "UTF-8"; |
||||||
|
|
||||||
|
private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; |
||||||
|
|
||||||
|
private Charset charset; |
||||||
|
|
||||||
|
private RSAPublicKey publicKey; |
||||||
|
|
||||||
|
private RSAPrivateKey privateKey; |
||||||
|
|
||||||
|
private Charset defaultCharset; |
||||||
|
|
||||||
|
public RsaRawEncryptor(RsaAlgorithm algorithm) { |
||||||
|
this(RsaKeyHelper.generateKeyPair(), algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor() { |
||||||
|
this(RsaKeyHelper.generateKeyPair()); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(KeyPair keyPair) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate()); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(String pemData) { |
||||||
|
this(RsaKeyHelper.parseKeyPair(pemData)); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(PublicKey publicKey) { |
||||||
|
this(DEFAULT_ENCODING, publicKey, null); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) { |
||||||
|
this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm) { |
||||||
|
this.charset = Charset.forName(encoding); |
||||||
|
this.publicKey = (RSAPublicKey) publicKey; |
||||||
|
this.privateKey = (RSAPrivateKey) privateKey; |
||||||
|
this.defaultCharset = Charset.forName(DEFAULT_ENCODING); |
||||||
|
this.algorithm = algorithm; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getPublicKey() { |
||||||
|
return RsaKeyHelper.encodePublicKey(this.publicKey, "application"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String encrypt(String text) { |
||||||
|
return new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), this.defaultCharset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String decrypt(String encryptedText) { |
||||||
|
if (this.privateKey == null) { |
||||||
|
throw new IllegalStateException("Private key must be provided for decryption"); |
||||||
|
} |
||||||
|
return new String(decrypt(Base64.getDecoder().decode(encryptedText.getBytes(this.defaultCharset))), |
||||||
|
this.charset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte[] encrypt(byte[] byteArray) { |
||||||
|
return encrypt(byteArray, this.publicKey, this.algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte[] decrypt(byte[] encryptedByteArray) { |
||||||
|
return decrypt(encryptedByteArray, this.privateKey, this.algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg) { |
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); |
||||||
|
try { |
||||||
|
final Cipher cipher = Cipher.getInstance(alg.getJceName()); |
||||||
|
int limit = Math.min(text.length, alg.getMaxLength()); |
||||||
|
int pos = 0; |
||||||
|
while (pos < text.length) { |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key); |
||||||
|
cipher.update(text, pos, limit); |
||||||
|
pos += limit; |
||||||
|
limit = Math.min(text.length - pos, alg.getMaxLength()); |
||||||
|
byte[] buffer = cipher.doFinal(); |
||||||
|
output.write(buffer, 0, buffer.length); |
||||||
|
} |
||||||
|
return output.toByteArray(); |
||||||
|
} |
||||||
|
catch (RuntimeException ex) { |
||||||
|
throw ex; |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new IllegalStateException("Cannot encrypt", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] decrypt(byte[] text, RSAPrivateKey key, RsaAlgorithm alg) { |
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); |
||||||
|
try { |
||||||
|
final Cipher cipher = Cipher.getInstance(alg.getJceName()); |
||||||
|
int maxLength = getByteLength(key); |
||||||
|
int pos = 0; |
||||||
|
while (pos < text.length) { |
||||||
|
int limit = Math.min(text.length - pos, maxLength); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key); |
||||||
|
cipher.update(text, pos, limit); |
||||||
|
pos += limit; |
||||||
|
byte[] buffer = cipher.doFinal(); |
||||||
|
output.write(buffer, 0, buffer.length); |
||||||
|
} |
||||||
|
return output.toByteArray(); |
||||||
|
} |
||||||
|
catch (RuntimeException ex) { |
||||||
|
throw ex; |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new IllegalStateException("Cannot decrypt", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// copied from sun.security.rsa.RSACore.getByteLength(java.math.BigInteger)
|
||||||
|
public static int getByteLength(RSAKey key) { |
||||||
|
int n = key.getModulus().bitLength(); |
||||||
|
return (n + 7) >> 3; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,247 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.security.KeyPair; |
||||||
|
import java.security.PrivateKey; |
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
import java.util.Base64; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
|
||||||
|
import org.springframework.security.crypto.codec.Hex; |
||||||
|
import org.springframework.security.crypto.keygen.KeyGenerators; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* @since 6.3 |
||||||
|
*/ |
||||||
|
public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder { |
||||||
|
|
||||||
|
private static final String DEFAULT_ENCODING = "UTF-8"; |
||||||
|
|
||||||
|
// The secret for encryption is random (so dictionary attack is not a danger)
|
||||||
|
private static final String DEFAULT_SALT = "deadbeef"; |
||||||
|
|
||||||
|
private final String salt; |
||||||
|
|
||||||
|
private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; |
||||||
|
|
||||||
|
private final Charset charset; |
||||||
|
|
||||||
|
private final PublicKey publicKey; |
||||||
|
|
||||||
|
private final PrivateKey privateKey; |
||||||
|
|
||||||
|
private final Charset defaultCharset; |
||||||
|
|
||||||
|
private final boolean gcm; |
||||||
|
|
||||||
|
public RsaSecretEncryptor(RsaAlgorithm algorithm, String salt, boolean gcm) { |
||||||
|
this(RsaKeyHelper.generateKeyPair(), algorithm, salt, gcm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(RsaAlgorithm algorithm, String salt) { |
||||||
|
this(RsaKeyHelper.generateKeyPair(), algorithm, salt); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(RsaAlgorithm algorithm, boolean gcm) { |
||||||
|
this(RsaKeyHelper.generateKeyPair(), algorithm, DEFAULT_SALT, gcm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(RsaAlgorithm algorithm) { |
||||||
|
this(RsaKeyHelper.generateKeyPair(), algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor() { |
||||||
|
this(RsaKeyHelper.generateKeyPair()); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt, boolean gcm) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, salt, gcm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, salt, false); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(KeyPair keyPair) { |
||||||
|
this(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate()); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm, String salt) { |
||||||
|
this(RsaKeyHelper.parseKeyPair(pemData), algorithm, salt); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm) { |
||||||
|
this(RsaKeyHelper.parseKeyPair(pemData), algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String pemData) { |
||||||
|
this(RsaKeyHelper.parseKeyPair(pemData)); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt, boolean gcm) { |
||||||
|
this(DEFAULT_ENCODING, publicKey, null, algorithm, salt, gcm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt) { |
||||||
|
this(DEFAULT_ENCODING, publicKey, null, algorithm, salt, false); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm) { |
||||||
|
this(DEFAULT_ENCODING, publicKey, null, algorithm); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(PublicKey publicKey) { |
||||||
|
this(DEFAULT_ENCODING, publicKey, null); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) { |
||||||
|
this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm) { |
||||||
|
this(encoding, publicKey, privateKey, algorithm, DEFAULT_SALT, false); |
||||||
|
} |
||||||
|
|
||||||
|
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm, |
||||||
|
String salt, boolean gcm) { |
||||||
|
this.charset = Charset.forName(encoding); |
||||||
|
this.publicKey = publicKey; |
||||||
|
this.privateKey = privateKey; |
||||||
|
this.defaultCharset = Charset.forName(DEFAULT_ENCODING); |
||||||
|
this.algorithm = algorithm; |
||||||
|
this.salt = isHex(salt) ? salt : new String(Hex.encode(salt.getBytes(this.defaultCharset))); |
||||||
|
this.gcm = gcm; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getPublicKey() { |
||||||
|
return RsaKeyHelper.encodePublicKey((RSAPublicKey) this.publicKey, "application"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String encrypt(String text) { |
||||||
|
return new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), this.defaultCharset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String decrypt(String encryptedText) { |
||||||
|
if (!canDecrypt()) { |
||||||
|
throw new IllegalStateException("Encryptor is not configured for decryption"); |
||||||
|
} |
||||||
|
return new String(decrypt(Base64.getDecoder().decode(encryptedText.getBytes(this.defaultCharset))), |
||||||
|
this.charset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte[] encrypt(byte[] byteArray) { |
||||||
|
return encrypt(byteArray, this.publicKey, this.algorithm, this.salt, this.gcm); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte[] decrypt(byte[] encryptedByteArray) { |
||||||
|
if (!canDecrypt()) { |
||||||
|
throw new IllegalStateException("Encryptor is not configured for decryption"); |
||||||
|
} |
||||||
|
return decrypt(encryptedByteArray, this.privateKey, this.algorithm, this.salt, this.gcm); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg, String salt, boolean gcm) { |
||||||
|
byte[] random = KeyGenerators.secureRandom(16).generateKey(); |
||||||
|
BytesEncryptor aes = gcm ? Encryptors.stronger(new String(Hex.encode(random)), salt) |
||||||
|
: Encryptors.standard(new String(Hex.encode(random)), salt); |
||||||
|
try { |
||||||
|
final Cipher cipher = Cipher.getInstance(alg.getJceName()); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key); |
||||||
|
byte[] secret = cipher.doFinal(random); |
||||||
|
ByteArrayOutputStream result = new ByteArrayOutputStream(text.length + 20); |
||||||
|
writeInt(result, secret.length); |
||||||
|
result.write(secret); |
||||||
|
result.write(aes.encrypt(text)); |
||||||
|
return result.toByteArray(); |
||||||
|
} |
||||||
|
catch (RuntimeException ex) { |
||||||
|
throw ex; |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new IllegalStateException("Cannot encrypt", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void writeInt(ByteArrayOutputStream result, int length) throws IOException { |
||||||
|
byte[] data = new byte[2]; |
||||||
|
data[0] = (byte) ((length >> 8) & 0xFF); |
||||||
|
data[1] = (byte) (length & 0xFF); |
||||||
|
result.write(data); |
||||||
|
} |
||||||
|
|
||||||
|
private static int readInt(ByteArrayInputStream result) throws IOException { |
||||||
|
byte[] b = new byte[2]; |
||||||
|
result.read(b); |
||||||
|
return ((b[0] & 0xFF) << 8) | (b[1] & 0xFF); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] decrypt(byte[] text, PrivateKey key, RsaAlgorithm alg, String salt, boolean gcm) { |
||||||
|
ByteArrayInputStream input = new ByteArrayInputStream(text); |
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length); |
||||||
|
try { |
||||||
|
int length = readInt(input); |
||||||
|
byte[] random = new byte[length]; |
||||||
|
input.read(random); |
||||||
|
final Cipher cipher = Cipher.getInstance(alg.getJceName()); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key); |
||||||
|
String secret = new String(Hex.encode(cipher.doFinal(random))); |
||||||
|
byte[] buffer = new byte[text.length - random.length - 2]; |
||||||
|
input.read(buffer); |
||||||
|
BytesEncryptor aes = gcm ? Encryptors.stronger(secret, salt) : Encryptors.standard(secret, salt); |
||||||
|
output.write(aes.decrypt(buffer)); |
||||||
|
return output.toByteArray(); |
||||||
|
} |
||||||
|
catch (RuntimeException ex) { |
||||||
|
throw ex; |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
throw new IllegalStateException("Cannot decrypt", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isHex(String input) { |
||||||
|
try { |
||||||
|
Hex.decode(input); |
||||||
|
return true; |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean canDecrypt() { |
||||||
|
return this.privateKey != null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class KeyStoreKeyFactoryTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void initializeEncryptorFromKeyStore() { |
||||||
|
char[] password = "foobar".toCharArray(); |
||||||
|
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), password); |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("test")); |
||||||
|
assertThat(encryptor.canDecrypt()).as("Should be able to decrypt").isTrue(); |
||||||
|
assertThat(encryptor.decrypt(encryptor.encrypt("foo"))).isEqualTo("foo"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void initializeEncryptorFromPkcs12KeyStore() { |
||||||
|
char[] password = "letmein".toCharArray(); |
||||||
|
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("keystore.pkcs12"), password); |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("mytestkey")); |
||||||
|
assertThat(encryptor.canDecrypt()).as("Should be able to decrypt").isTrue(); |
||||||
|
assertThat(encryptor.decrypt(encryptor.encrypt("foo"))).isEqualTo("foo"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void initializeEncryptorFromTrustedCertificateInKeyStore() { |
||||||
|
char[] password = "foobar".toCharArray(); |
||||||
|
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), password); |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("testcertificate")); |
||||||
|
assertThat(encryptor.canDecrypt()).as("Should not be able to decrypt").isFalse(); |
||||||
|
assertThat(encryptor.encrypt("foo")).isNotEqualTo("foo"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void initializeEncryptorFromTrustedCertificateInPkcs12KeyStore() { |
||||||
|
char[] password = "letmein".toCharArray(); |
||||||
|
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("keystore.pkcs12"), password); |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair("mytestcertificate")); |
||||||
|
assertThat(encryptor.canDecrypt()).as("Should not be able to decrypt").isFalse(); |
||||||
|
assertThat(encryptor.encrypt("foo")).isNotEqualTo("foo"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.security.KeyPair; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.util.StreamUtils; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
public class RsaKeyHelperTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void parsePrivateKey() throws Exception { |
||||||
|
// ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem
|
||||||
|
String pem = StreamUtils.copyToString(new ClassPathResource("/fake.pem", getClass()).getInputStream(), |
||||||
|
StandardCharsets.UTF_8); |
||||||
|
KeyPair result = RsaKeyHelper.parseKeyPair(pem); |
||||||
|
assertThat(result.getPrivate().getEncoded().length > 0).isTrue(); |
||||||
|
assertThat(result.getPrivate().getAlgorithm()).isEqualTo("RSA"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void parseSpaceyKey() throws Exception { |
||||||
|
String pem = StreamUtils.copyToString(new ClassPathResource("/spacey.pem", getClass()).getInputStream(), |
||||||
|
StandardCharsets.UTF_8); |
||||||
|
KeyPair result = RsaKeyHelper.parseKeyPair(pem); |
||||||
|
assertThat(result.getPrivate().getEncoded().length > 0).isTrue(); |
||||||
|
assertThat(result.getPrivate().getAlgorithm()).isEqualTo("RSA"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void parseBadKey() throws Exception { |
||||||
|
// ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem
|
||||||
|
String pem = StreamUtils.copyToString(new ClassPathResource("/bad.pem", getClass()).getInputStream(), |
||||||
|
StandardCharsets.UTF_8); |
||||||
|
try { |
||||||
|
RsaKeyHelper.parseKeyPair(pem); |
||||||
|
throw new IllegalStateException("Expected IllegalArgumentException"); |
||||||
|
} |
||||||
|
catch (IllegalArgumentException ex) { |
||||||
|
assertThat(ex.getMessage().contains("PEM")).isTrue(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,154 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class RsaRawEncryptorTests { |
||||||
|
|
||||||
|
private RsaRawEncryptor encryptor = new RsaRawEncryptor(); |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
public void init() { |
||||||
|
LONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING; |
||||||
|
for (int i = 0; i < 4; i++) { |
||||||
|
LONG_STRING = LONG_STRING + LONG_STRING; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTrip() { |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripOeap() { |
||||||
|
this.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripLongString() { |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripLongStringOeap() { |
||||||
|
this.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTrip2048Key() { |
||||||
|
String pemData = "-----BEGIN RSA PRIVATE KEY-----" |
||||||
|
+ "MIIEpQIBAAKCAQEA5KHEkCudAHCKIUHKyW6Z8dMyQsKrLbpDe0wDzx9MBARcOoS9" |
||||||
|
+ "ZUjzXwK6p/0RM6aCp+b9kkr37QKQ9K/Am13sr0z8Mkn1Q2cvXiL5gbnY1nYGk8/m" |
||||||
|
+ "CBX3QEhH2UII4yJsDVx1xmcSorZaWmeNKor7Zl3SZaQpWTvlkMgQKwY8DZL6PPxt" |
||||||
|
+ "JRPeKmuUY6B59u5okh1G6Y9OnT2dVxAkqT8WgLHu6StxBmueJ272x2sUWUzoDhnP" |
||||||
|
+ "7JRqa7h7t6fml3o3Op1iCywCOFzCIcK6G/oG/WZ7tbBYkwQdDjn/9VMdKkkPufwq" |
||||||
|
+ "zt4S75NJygXDwDnNPiTVoaOwrRrL8ahgw6bFCQIDAQABAoIBAECIMHUI+l2fZj2Q" |
||||||
|
+ "1m4Ym7cYB320eKCFjHqGsCSMDuarXGTgBp1KA/dzS8ASvAI6I3LEzhm2s1fge420" |
||||||
|
+ "9cZksmOgdSa0nVeTDlmhwY8OJ9gQpDagXas2l/066Zy2+M8zbhAvYsbHXQk0MziF" |
||||||
|
+ "NeEmLWNtY+9wcINRVrCQ549dSSIDK6UX21oU6d1mrlnF5/bbbdDIM3dKok355jwx" |
||||||
|
+ "0HFY0tJIs1zArsBVoz3Ccu1MQEfnxEFM1LLPi5rE6cuHIOBinbD1OQ2R/HM2aukG" |
||||||
|
+ "Rk2m6F3wAieJ7zpt5yaHuuIedn8p8m2NVulXAjgkY2oQl3GGiDH/H7eZlrvQRg6E" |
||||||
|
+ "D8Bq+ykCgYEA+AfPXVeeVg3Qu0KsNrACek/o92BMY9g3GyPVGULGvq9seoNB86hj" |
||||||
|
+ "nXasqngBfTlOfJFiahoEzRBB9hIyo1zMw4x99pR8nGxhR3aU+v8EGftMABGHWsB9" |
||||||
|
+ "Jxj4YQH4fhi57iBa72QmNPbu/1o7y3SEe68E5PJ8KY3jc4xos8Vl658CgYEA6/pk" |
||||||
|
+ "t6WZII+9lpxQfePQDIlBWAphiQceh995bGXfDmX3vOVmPozix9/fUtF1TeKS/ypw" |
||||||
|
+ "u++Qmvj5oMsBVrjCyoOYfHKE2vGrLoEzkX/sPO65IsV00geZZoyCEKEE3USJfY46" |
||||||
|
+ "u0hs61oP8HJyLhLiYiGcFTzZ4nEvvEbiM4E/DlcCgYEA6S0OecZhiK08SpAHrvIR" |
||||||
|
+ "okN11PqnVkZyqAUr1a+9gI8TAKpdWmA4JlTnRuvDGqLBcsKLLwx+7voVyOyaxpH7" |
||||||
|
+ "vutZkHNQIw6Q9co5jS4qAPMLJBVWlq7X+eWzvB9KKeG9Cm1IkD4q3Sg4z79Y75D+" |
||||||
|
+ "6/hCNarxp29JIdwior81bikCgYEApp1P+b7pxGzZPvs1df2hCwjqY0BJJ5goPWVT" |
||||||
|
+ "dW7kNGVYqz4JmAafpOJz6yTLP2fHxHRxzrBSmKlMj/RmCJZBqv2Jb+zn0zMpW5eM" |
||||||
|
+ "EqKQ6WDgxSVH23fUHuz8dMNMDPL0ZPtEirGTfgVEFdCov9FDmGgErZYefVzPiI8/" |
||||||
|
+ "7X/HRtcCgYEApQ2YS+0DLPqaM0cC6/6hDr/jmHLFhHaV6DZR7M9HHDnMN2uMlOEa" |
||||||
|
+ "RYvXRMBjyQ7LQkwOj6K5k8MVrsDDM5dbekTBgcJMHfM9uViDkB0VPYULORmDJ20N" |
||||||
|
+ "MLowIAiSon2B2/isatY80YtFq+bRyvPOzjGvinHN3MU1GH/gFuS0fiw=" + "-----END RSA PRIVATE KEY-----"; |
||||||
|
RsaRawEncryptor encryptor_2048 = new RsaRawEncryptor(pemData); |
||||||
|
assertThat(encryptor_2048.decrypt(encryptor_2048.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTrip4096Key() { |
||||||
|
String pemData = "-----BEGIN RSA PRIVATE KEY-----" |
||||||
|
+ "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" + "-----END RSA PRIVATE KEY-----"; |
||||||
|
RsaRawEncryptor encryptor_4096 = new RsaRawEncryptor(pemData); |
||||||
|
assertThat(encryptor_4096.decrypt(encryptor_4096.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
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."; |
||||||
|
|
||||||
|
private static String LONG_STRING; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,121 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2013-2024 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.security.crypto.encrypt; |
||||||
|
|
||||||
|
import java.security.PublicKey; |
||||||
|
import java.security.interfaces.RSAPublicKey; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Dave Syer |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class RsaSecretEncryptorTests { |
||||||
|
|
||||||
|
private RsaSecretEncryptor encryptor = new RsaSecretEncryptor(); |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
public void init() { |
||||||
|
LONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING; |
||||||
|
for (int i = 0; i < 4; i++) { |
||||||
|
LONG_STRING = LONG_STRING + LONG_STRING; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripKey() { |
||||||
|
PublicKey key = RsaKeyHelper.generateKeyPair().getPublic(); |
||||||
|
String encoded = RsaKeyHelper.encodePublicKey((RSAPublicKey) key, "application"); |
||||||
|
assertThat(RsaKeyHelper.parsePublicKey(encoded)).isEqualTo(key); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTrip() { |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithSalt() { |
||||||
|
this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "somesalt"); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithHexSalt() { |
||||||
|
this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "beefea"); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithLongSalt() { |
||||||
|
this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, "somesaltsomesaltsomesaltsomesaltsomesalt"); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripOaep() { |
||||||
|
this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripOaepGcm() { |
||||||
|
this.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, true); |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithMixedAlgorithm() { |
||||||
|
RsaSecretEncryptor oaep = new RsaSecretEncryptor(RsaAlgorithm.OAEP); |
||||||
|
assertThatIllegalStateException().isThrownBy(() -> oaep.decrypt(this.encryptor.encrypt("encryptor"))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithMixedSalt() { |
||||||
|
RsaSecretEncryptor other = new RsaSecretEncryptor(this.encryptor.getPublicKey(), RsaAlgorithm.DEFAULT, "salt"); |
||||||
|
assertThatIllegalStateException().isThrownBy(() -> this.encryptor.decrypt(other.encrypt("encryptor"))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripWithPublicKeyEncryption() { |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(this.encryptor.getPublicKey()); |
||||||
|
RsaSecretEncryptor decryptor = this.encryptor; |
||||||
|
assertThat(decryptor.decrypt(encryptor.encrypt("encryptor"))).isEqualTo("encryptor"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void publicKeyCannotDecrypt() { |
||||||
|
RsaSecretEncryptor encryptor = new RsaSecretEncryptor(this.encryptor.getPublicKey()); |
||||||
|
assertThat(encryptor.canDecrypt()).as("Encryptor schould not be able to decrypt").isFalse(); |
||||||
|
assertThatIllegalStateException().isThrownBy(() -> encryptor.decrypt(encryptor.encrypt("encryptor"))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void roundTripLongString() { |
||||||
|
assertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING); |
||||||
|
} |
||||||
|
|
||||||
|
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."; |
||||||
|
|
||||||
|
private static String LONG_STRING; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIICWwIBAAKBgQDMWnfaQ0yLFXelprq2S8UurnaGvxFNUdbmTyJeycem5vGLycEY |
||||||
|
T4KcdVCTU5491cjbk5GcHjoj2efRSO0y0aXIlUJpLofDdML/SuGLZWp/GbEv978M |
||||||
|
pZIztK8iaIm7D/D7by8aws1RJyD9T+lZDAGY7eFfMp0EQyHOcEL0NGFLuwIDAQAB |
||||||
|
AoGAWwC6uO8ZaiKwOouqQD4z3FsDG3SA/v7ABaYd9zpCd9gGnyrEm8/kqUoxDLrD |
||||||
|
EGRg4y+vO2fWmlqSuoeQYf4spf+vi2di+mGIb6nGe7TpMLPa7lFLOSQHZRx5M5H6 |
||||||
|
JDhfhAHlKmF9gLGvDHbpyErzn5YXjcu0PoFiNC1y445D8iECQQDvJzkGbJ9l9vb0 |
||||||
|
oRyGXRDpddUcVMECLLB9NKmTl/zKy/qVPD+zYNoi87ePBJFbgmAXRjhhTk2uSBRP |
||||||
|
NtVaMoXLAkEA2r+ugzjsLZQIYz/9gxdzdbKWDgpSPbhKCR4bOmrDgJMcOVjtwW+n |
||||||
|
+liaX6zwI0QEgCAWLzCbbYDmj3kJrRwT0QJAaowg/dm7EmR7FfYJjVs9Q6X5skuY |
||||||
|
Se27G60wt88JExjZpU9YWgSWaugGKbOxRwHI6dWhHMkUFseKNNiLKUpFDQJALIGP |
||||||
|
ahdsxiE2S6s7Uy60SSAas6SZ8wDJ320GsS4DtOc5eNmFFjQ3gxH/5rNy8FnoaIEe |
||||||
|
wl8rYG43er1voI7z4QJAB4qaqBo7eeiRgnUVIccaSZkNIMSrZ9QUjVFRgfLwAXDO |
||||||
|
Ae+t6V+eB0oaIXczA+BLj3Oe6D3iHRGHrxGlcvDdHw== |
||||||
|
-----END RSA PRIVATE KEY----- |
||||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,25 @@ |
|||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 |
||||||
|
4YbrwfKwjoTjk1M6gLQpOA4Blocx6zN5OnICnVGlVM9xymWxTxxCfc2tE2Fai9I1wchULCChhwm/UU5ZNi3KpXinlyamSYw+lMQkZ8gTXCgOEvs2j9E1quF4pvy1BZKvbD8tUnUQlyiKRnI6gOxQL8B6OAYPRdaa9FVNmrs1B4eDPG918L2f1pT090P1n+tw |
||||||
|
iejNgQvtSD78/A88qt89OhzscsufALTrBjycn89kkfBd0zbVLF0W6+ZVLZrf97/y |
||||||
|
LCoGSCcZL9LFPNvNqxOnleviDco7aOs4stQ9jQIDAQABAoIBAQC1TbthyN0YUe+T |
||||||
|
7dIDAbbZaVrU00biOtXgzjMADmTprP7Hf18UpIIIKfzfWw6FUD+gc1t4oe5pogE9 |
||||||
|
UwGMXUmOORxu2pMYTb5vT9CEdexYnsAZsCo8PdD9GYSNrmquQef2MFpEqYQmHrdC |
||||||
|
KWpaXn2i1ak+iCRPUGp4YwHpynZVxfE8z/AIsPn6NPDh6SnCXb1rTgQe2UCfXm93 |
||||||
|
UJe5F/OR2kQi5KFO+dxLmCOBCwr6SGCLH+VotGpuxCVRUd9sJ/d4QpDZEgjuf7Ug |
||||||
|
eQHfgMDS/tc09B9rl0dwKnEa31kcQ9X9KLkKP+w0Pqhh0Emny20eg9jS6XNayg61 |
||||||
|
p/LQtW9BAoGBAO5veKMIcXfZmuh11WIIhdhLKkNtYyt5NDmrV8/IVScLFvjB0ftt |
||||||
|
8PAtXo/ekOHkyITyIumQ9l4VCvacNw7DyV9FYk4WvrvVYOCL8aZi+O5+12NT67eO |
||||||
|
Rr/voGlRoV05X7+inc90qbbYJ8lRmLSqvzmsm98mkuhw/FKGRhVZIfAJAoGBAM5R |
||||||
|
I5vK6cJxOwXQOEGOd5/8B9JMFXyuendXo/N2/NxSQsbx4pc3v2rv/eGJYaY7Nx/y |
||||||
|
2M/vdWYkpG59PAS3k2TrCA/0SGmyVqY+c8BomKisU5VaBlIPfGuec9tDPgWCp8Ur |
||||||
|
3Jjt/2sVoa0vMkqymUqMb9HyH9tdI9oyh7EOOrplAoGAR6DlNNUMgVy11K/Rcqns |
||||||
|
y5WJFMh/ykeXENwQfTNJoXkLZZ+UXVwhzYVTqxTJoZMBSi8TnecWnBzmNj+nqp/W |
||||||
|
lvBZH+xlUDhB6jMgXUPOVJd2TTigz3vGdVKfdgQ33bGmugM4NWJuuacmDKyem2fQ |
||||||
|
GptoGBmWeI24v3HnC/LC50ECgYAz0iN8hRnz0db+Xc9TgAJB997LDnszJuvxv9yZ |
||||||
|
UWCvwiWtrKG6U7FLnd4J4STayPLOnoOgrsexETEP43rIwIdQCMysnTH3AmlLNlKC |
||||||
|
mIMHksknsUX3JJaevVziTOBuJ+QV3S96ZgUKk5NZWYprQrLIC8AmXodr5NgVfS2h |
||||||
|
5i4QFQKBgFfbYHiMw5AAUQrBNkrAjLd1wIaO/6qS3w4OsCWKowhfaJLEXAbIRV7s |
||||||
|
vAtgtlCovdasVj4RRLXFf+73naVTQjBZI+3jWHHyFk3+Zy86mQCSGv9WuDVV1IhS |
||||||
|
h8InTVvK8wgdgX7qiw3pvU0roqNW4/j4j8OqJO3Zt4KO2iX8htsO |
||||||
|
-----END RSA PRIVATE KEY----- |
||||||
Loading…
Reference in new issue