95 lines
3.3 KiB
Java
95 lines
3.3 KiB
Java
|
package org.keycloak.crypto;
|
||
|
|
||
|
import org.bouncycastle.asn1.ASN1InputStream;
|
||
|
import org.bouncycastle.asn1.ASN1Integer;
|
||
|
import org.bouncycastle.asn1.ASN1Primitive;
|
||
|
import org.bouncycastle.asn1.ASN1Sequence;
|
||
|
import org.bouncycastle.asn1.DERSequenceGenerator;
|
||
|
import org.bouncycastle.asn1.x9.X9IntegerConverter;
|
||
|
import org.keycloak.common.VerificationException;
|
||
|
import org.keycloak.models.KeycloakSession;
|
||
|
|
||
|
import java.io.ByteArrayOutputStream;
|
||
|
import java.io.IOException;
|
||
|
import java.math.BigInteger;
|
||
|
|
||
|
public class ECDSASignatureProvider implements SignatureProvider {
|
||
|
|
||
|
private final KeycloakSession session;
|
||
|
private final String algorithm;
|
||
|
|
||
|
public ECDSASignatureProvider(KeycloakSession session, String algorithm) {
|
||
|
this.session = session;
|
||
|
this.algorithm = algorithm;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public SignatureSignerContext signer() throws SignatureException {
|
||
|
return new ServerECDSASignatureSignerContext(session, algorithm);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public SignatureVerifierContext verifier(String kid) throws VerificationException {
|
||
|
return new ServerECDSASignatureVerifierContext(session, kid, algorithm);
|
||
|
}
|
||
|
|
||
|
public static byte[] concatenatedRSToASN1DER(final byte[] signature, int signLength) throws IOException {
|
||
|
int len = signLength / 2;
|
||
|
int arraySize = len + 1;
|
||
|
|
||
|
byte[] r = new byte[arraySize];
|
||
|
byte[] s = new byte[arraySize];
|
||
|
System.arraycopy(signature, 0, r, 1, len);
|
||
|
System.arraycopy(signature, len, s, 1, len);
|
||
|
BigInteger rBigInteger = new BigInteger(r);
|
||
|
BigInteger sBigInteger = new BigInteger(s);
|
||
|
|
||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||
|
DERSequenceGenerator seqGen = new DERSequenceGenerator(bos);
|
||
|
|
||
|
seqGen.addObject(new ASN1Integer(rBigInteger.toByteArray()));
|
||
|
seqGen.addObject(new ASN1Integer(sBigInteger.toByteArray()));
|
||
|
seqGen.close();
|
||
|
bos.close();
|
||
|
|
||
|
return bos.toByteArray();
|
||
|
}
|
||
|
|
||
|
public static byte[] asn1derToConcatenatedRS(final byte[] derEncodedSignatureValue, int signLength) throws IOException {
|
||
|
int len = signLength / 2;
|
||
|
|
||
|
ASN1InputStream asn1InputStream = new ASN1InputStream(derEncodedSignatureValue);
|
||
|
ASN1Primitive asn1Primitive = asn1InputStream.readObject();
|
||
|
asn1InputStream.close();
|
||
|
|
||
|
ASN1Sequence asn1Sequence = (ASN1Sequence.getInstance(asn1Primitive));
|
||
|
ASN1Integer rASN1 = (ASN1Integer) asn1Sequence.getObjectAt(0);
|
||
|
ASN1Integer sASN1 = (ASN1Integer) asn1Sequence.getObjectAt(1);
|
||
|
X9IntegerConverter x9IntegerConverter = new X9IntegerConverter();
|
||
|
byte[] r = x9IntegerConverter.integerToBytes(rASN1.getValue(), len);
|
||
|
byte[] s = x9IntegerConverter.integerToBytes(sASN1.getValue(), len);
|
||
|
|
||
|
byte[] concatenatedSignatureValue = new byte[signLength];
|
||
|
System.arraycopy(r, 0, concatenatedSignatureValue, 0, len);
|
||
|
System.arraycopy(s, 0, concatenatedSignatureValue, len, len);
|
||
|
|
||
|
return concatenatedSignatureValue;
|
||
|
}
|
||
|
|
||
|
public enum ECDSA {
|
||
|
ES256(64),
|
||
|
ES384(96),
|
||
|
ES512(132);
|
||
|
|
||
|
private final int signatureLength;
|
||
|
|
||
|
ECDSA(int signatureLength) {
|
||
|
this.signatureLength = signatureLength;
|
||
|
}
|
||
|
|
||
|
public int getSignatureLength() {
|
||
|
return this.signatureLength;
|
||
|
}
|
||
|
}
|
||
|
}
|