Skip to content

Commit b95d12a

Browse files
committed
Add AuthzClientCryptoProvider to authz-client in keycloak main repository
closes #33831 Signed-off-by: mposolda <[email protected]>
1 parent 10aca55 commit b95d12a

File tree

12 files changed

+673
-9
lines changed

12 files changed

+673
-9
lines changed

authz/client/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@
5858
<artifactId>jackson-annotations</artifactId>
5959
<scope>provided</scope>
6060
</dependency>
61+
62+
<dependency>
63+
<groupId>junit</groupId>
64+
<artifactId>junit</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.hamcrest</groupId>
69+
<artifactId>hamcrest</artifactId>
70+
<scope>test</scope>
71+
</dependency>
6172
</dependencies>
6273

6374
<build>

authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.keycloak.authorization.client.resource.ProtectionResource;
3131
import org.keycloak.authorization.client.util.Http;
3232
import org.keycloak.authorization.client.util.TokenCallable;
33+
import org.keycloak.common.crypto.CryptoIntegration;
3334
import org.keycloak.common.util.KeycloakUriBuilder;
3435
import org.keycloak.representations.AccessTokenResponse;
3536
import org.keycloak.util.SystemPropertiesJsonParserFactory;
@@ -91,6 +92,7 @@ public static AuthzClient create(InputStream configStream) throws RuntimeExcepti
9192
* @return a new instance
9293
*/
9394
public static AuthzClient create(Configuration configuration) {
95+
CryptoIntegration.init(AuthzClient.class.getClassLoader());
9496
return new AuthzClient(configuration);
9597
}
9698

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* Copyright 2024 Red Hat, Inc. and/or its affiliates
3+
* and other contributors as indicated by the @author tags.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
*
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.keycloak.authorization.client.util.crypto;
21+
22+
import java.io.ByteArrayInputStream;
23+
import java.io.EOFException;
24+
import java.io.IOException;
25+
import java.math.BigInteger;
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
/**
30+
*
31+
* @author rmartinc
32+
*/
33+
class ASN1Decoder {
34+
35+
private final ByteArrayInputStream is;
36+
private final int limit;
37+
private int count;
38+
39+
ASN1Decoder(byte[] bytes) {
40+
is = new ByteArrayInputStream(bytes);
41+
count = 0;
42+
limit = bytes.length;
43+
}
44+
45+
public static ASN1Decoder create(byte[] bytes) {
46+
return new ASN1Decoder(bytes);
47+
}
48+
49+
public List<byte[]> readSequence() throws IOException {
50+
int tag = readTag();
51+
int tagNo = readTagNumber(tag);
52+
if (tagNo != ASN1Encoder.SEQUENCE) {
53+
throw new IOException("Invalid Sequence tag " + tagNo);
54+
}
55+
int length = readLength();
56+
List<byte[]> result = new ArrayList<>();
57+
while (length > 0) {
58+
byte[] bytes = readNext();
59+
result.add(bytes);
60+
length = length - bytes.length;
61+
}
62+
return result;
63+
}
64+
65+
public BigInteger readInteger() throws IOException {
66+
int tag = readTag();
67+
int tagNo = readTagNumber(tag);
68+
if (tagNo != ASN1Encoder.INTEGER) {
69+
throw new IOException("Invalid Integer tag " + tagNo);
70+
}
71+
int length = readLength();
72+
byte[] bytes = read(length);
73+
return new BigInteger(bytes);
74+
}
75+
76+
byte[] readNext() throws IOException {
77+
mark();
78+
int tag = readTag();
79+
readTagNumber(tag);
80+
int length = readLength();
81+
length += reset();
82+
return read(length);
83+
}
84+
85+
int readTag() throws IOException {
86+
int tag = read();
87+
if (tag < 0) {
88+
throw new EOFException("EOF found inside tag value.");
89+
}
90+
return tag;
91+
}
92+
93+
int readTagNumber(int tag) throws IOException {
94+
int tagNo = tag & 0x1f;
95+
96+
//
97+
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
98+
//
99+
if (tagNo == 0x1f) {
100+
tagNo = 0;
101+
102+
int b = read();
103+
104+
// X.690-0207 8.1.2.4.2
105+
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
106+
if ((b & 0x7f) == 0) // Note: -1 will pass
107+
{
108+
throw new IOException("corrupted stream - invalid high tag number found");
109+
}
110+
111+
while ((b >= 0) && ((b & 0x80) != 0)) {
112+
tagNo |= (b & 0x7f);
113+
tagNo <<= 7;
114+
b = read();
115+
}
116+
117+
if (b < 0) {
118+
throw new EOFException("EOF found inside tag value.");
119+
}
120+
121+
tagNo |= (b & 0x7f);
122+
}
123+
124+
return tagNo;
125+
}
126+
127+
int readLength() throws IOException {
128+
int length = read();
129+
if (length < 0) {
130+
throw new EOFException("EOF found when length expected");
131+
}
132+
133+
if (length == 0x80) {
134+
return -1; // indefinite-length encoding
135+
}
136+
137+
if (length > 127) {
138+
int size = length & 0x7f;
139+
140+
// Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
141+
if (size > 4) {
142+
throw new IOException("DER length more than 4 bytes: " + size);
143+
}
144+
145+
length = 0;
146+
for (int i = 0; i < size; i++) {
147+
int next = read();
148+
149+
if (next < 0) {
150+
throw new EOFException("EOF found reading length");
151+
}
152+
153+
length = (length << 8) + next;
154+
}
155+
156+
if (length < 0) {
157+
throw new IOException("corrupted stream - negative length found");
158+
}
159+
160+
if (length >= limit) // after all we must have read at least 1 byte
161+
{
162+
throw new IOException("corrupted stream - out of bounds length found");
163+
}
164+
}
165+
166+
return length;
167+
}
168+
169+
byte[] read(int length) throws IOException {
170+
byte[] bytes = new byte[length];
171+
int totalBytesRead = 0;
172+
173+
while (totalBytesRead < length) {
174+
int bytesRead = is.read(bytes, totalBytesRead, length - totalBytesRead);
175+
if (bytesRead == -1) {
176+
throw new IOException(String.format("EOF found reading %d bytes", length));
177+
}
178+
totalBytesRead += bytesRead;
179+
}
180+
count += length;
181+
return bytes;
182+
}
183+
184+
void mark() {
185+
count = 0;
186+
is.mark(is.available());
187+
}
188+
189+
int reset() {
190+
int tmp = count;
191+
is.reset();
192+
return tmp;
193+
}
194+
195+
int read() {
196+
int tmp = is.read();
197+
if (tmp >= 0) {
198+
count++;
199+
}
200+
return tmp;
201+
}
202+
}
203+
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2024 Red Hat, Inc. and/or its affiliates
3+
* and other contributors as indicated by the @author tags.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
*
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.keycloak.authorization.client.util.crypto;
21+
22+
import java.io.ByteArrayOutputStream;
23+
import java.io.IOException;
24+
import java.math.BigInteger;
25+
26+
/**
27+
*
28+
* @author rmartinc
29+
*/
30+
class ASN1Encoder {
31+
32+
static final int INTEGER = 0x02;
33+
static final int SEQUENCE = 0x10;
34+
static final int CONSTRUCTED = 0x20;
35+
36+
private final ByteArrayOutputStream os;
37+
38+
private ASN1Encoder() {
39+
this.os = new ByteArrayOutputStream();
40+
}
41+
42+
static public ASN1Encoder create() {
43+
return new ASN1Encoder();
44+
}
45+
46+
public ASN1Encoder write(BigInteger value) throws IOException {
47+
writeEncoded(INTEGER, value.toByteArray());
48+
return this;
49+
}
50+
51+
public ASN1Encoder writeDerSeq(ASN1Encoder... objects) throws IOException {
52+
writeEncoded(CONSTRUCTED | SEQUENCE, concatenate(objects));
53+
return this;
54+
}
55+
56+
public byte[] toByteArray() {
57+
return os.toByteArray();
58+
}
59+
60+
void writeEncoded(int tag, byte[] bytes) throws IOException {
61+
write(tag);
62+
writeLength(bytes.length);
63+
write(bytes);
64+
}
65+
66+
void writeLength(int length) throws IOException {
67+
if (length > 127) {
68+
int size = 1;
69+
int val = length;
70+
71+
while ((val >>>= 8) != 0) {
72+
size++;
73+
}
74+
75+
write((byte) (size | 0x80));
76+
77+
for (int i = (size - 1) * 8; i >= 0; i -= 8) {
78+
write((byte) (length >> i));
79+
}
80+
} else {
81+
write((byte) length);
82+
}
83+
}
84+
85+
void write(byte[] bytes) throws IOException {
86+
os.write(bytes);
87+
}
88+
89+
void write(int b) throws IOException {
90+
os.write(b);
91+
}
92+
93+
byte[] concatenate(ASN1Encoder... objects) throws IOException {
94+
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
95+
for (ASN1Encoder object : objects) {
96+
tmp.write(object.toByteArray());
97+
}
98+
return tmp.toByteArray();
99+
}
100+
}
101+

0 commit comments

Comments
 (0)