Skip to content

Commit e36fd0f

Browse files
chqrhierynomus
authored andcommitted
Add support for authentication with DSA & RSA user certificates (#153) (#319)
* Add support for authentication with DSA & RSA user certificates (#153) Updates: - KeyType.java - add support for two certificate key types [email protected] [email protected] - Buffer.java - allow uint64s that overflow Long.MAX_VALUE, otherwise we break on certificates with serial numbers greater Long.MAX_VALUE - OpenSSHKeyFile, KeyProviderUtil - prefer public key files that end "-cert.pub" if they exist Added new class Certificate, which represents certificate key Reference: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD * Use BigInteger for certificate serial numbers, address Codacy issues * Address code review concerns
1 parent 382321d commit e36fd0f

File tree

15 files changed

+791
-53
lines changed

15 files changed

+791
-53
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package com.hierynomus.sshj.userauth.certificate;
2+
3+
import java.math.BigInteger;
4+
import java.security.PublicKey;
5+
import java.util.Date;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
/*
10+
* Copyright (C)2009 - SSHJ Contributors
11+
*
12+
* Licensed under the Apache License, Version 2.0 (the "License");
13+
* you may not use this file except in compliance with the License.
14+
* You may obtain a copy of the License at
15+
*
16+
* http://www.apache.org/licenses/LICENSE-2.0
17+
*
18+
* Unless required by applicable law or agreed to in writing, software
19+
* distributed under the License is distributed on an "AS IS" BASIS,
20+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
* See the License for the specific language governing permissions and
22+
* limitations under the License.
23+
*/
24+
/**
25+
* Certificate wrapper for public keys, created to help implement
26+
* protocol described here:
27+
*
28+
* https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
29+
*
30+
* Consumed primarily by net.shmizz.sshj.common.KeyType
31+
*
32+
* @param <T> inner public key type
33+
*/
34+
public class Certificate<T extends PublicKey> implements PublicKey {
35+
private static final long serialVersionUID = 1L;
36+
37+
private final T publicKey;
38+
private final byte[] nonce;
39+
private final BigInteger serial;
40+
private final long type;
41+
private final String id;
42+
private final List<String> validPrincipals;
43+
private final Date validAfter;
44+
private final Date validBefore;
45+
private final Map<String, String> critOptions;
46+
private final Map<String, String> extensions;
47+
private final byte[] signatureKey;
48+
private final byte[] signature;
49+
50+
Certificate(Builder<T> builder) {
51+
this.publicKey = builder.getPublicKey();
52+
this.nonce = builder.getNonce();
53+
this.serial = builder.getSerial();
54+
this.type = builder.getType();
55+
this.id = builder.getId();
56+
this.validPrincipals = builder.getValidPrincipals();
57+
this.validAfter = builder.getValidAfter();
58+
this.validBefore = builder.getValidBefore();
59+
this.critOptions = builder.getCritOptions();
60+
this.extensions = builder.getExtensions();
61+
this.signatureKey = builder.getSignatureKey();
62+
this.signature = builder.getSignature();
63+
}
64+
65+
public static <P extends PublicKey> Builder<P> getBuilder() {
66+
return new Builder<P>();
67+
}
68+
69+
public byte[] getNonce() {
70+
return nonce;
71+
}
72+
73+
public BigInteger getSerial() {
74+
return serial;
75+
}
76+
77+
public long getType() {
78+
return type;
79+
}
80+
81+
public String getId() {
82+
return id;
83+
}
84+
85+
public List<String> getValidPrincipals() {
86+
return validPrincipals;
87+
}
88+
89+
public Date getValidAfter() {
90+
return validAfter;
91+
}
92+
93+
public Date getValidBefore() {
94+
return validBefore;
95+
}
96+
97+
public Map<String, String> getCritOptions() {
98+
return critOptions;
99+
}
100+
101+
public Map<String, String> getExtensions() {
102+
return extensions;
103+
}
104+
105+
public byte[] getSignatureKey() {
106+
return signatureKey;
107+
}
108+
109+
public byte[] getSignature() {
110+
return signature;
111+
}
112+
113+
public T getKey() {
114+
return publicKey;
115+
}
116+
117+
@Override
118+
public byte[] getEncoded() {
119+
return publicKey.getEncoded();
120+
}
121+
122+
@Override
123+
public String getAlgorithm() {
124+
return publicKey.getAlgorithm();
125+
}
126+
127+
@Override
128+
public String getFormat() {
129+
return publicKey.getFormat();
130+
}
131+
132+
public static class Builder<T extends PublicKey> {
133+
private T publicKey;
134+
private byte[] nonce;
135+
private BigInteger serial;
136+
private long type;
137+
private String id;
138+
private List<String> validPrincipals;
139+
private Date validAfter;
140+
private Date validBefore;
141+
private Map<String, String> critOptions;
142+
private Map<String, String> extensions;
143+
private byte[] signatureKey;
144+
private byte[] signature;
145+
146+
public Certificate<T> build() {
147+
return new Certificate<T>(this);
148+
}
149+
150+
public T getPublicKey() {
151+
return publicKey;
152+
}
153+
154+
public Builder<T> publicKey(T publicKey) {
155+
this.publicKey = publicKey;
156+
return this;
157+
}
158+
159+
public byte[] getNonce() {
160+
return nonce;
161+
}
162+
163+
public Builder<T> nonce(byte[] nonce) {
164+
this.nonce = nonce;
165+
return this;
166+
}
167+
168+
public BigInteger getSerial() {
169+
return serial;
170+
}
171+
172+
public Builder<T> serial(BigInteger serial) {
173+
this.serial = serial;
174+
return this;
175+
}
176+
177+
public long getType() {
178+
return type;
179+
}
180+
181+
public Builder<T> type(long type) {
182+
this.type = type;
183+
return this;
184+
}
185+
186+
public String getId() {
187+
return id;
188+
}
189+
190+
public Builder<T> id(String id) {
191+
this.id = id;
192+
return this;
193+
}
194+
195+
public List<String> getValidPrincipals() {
196+
return validPrincipals;
197+
}
198+
199+
public Builder<T> validPrincipals(List<String> validPrincipals) {
200+
this.validPrincipals = validPrincipals;
201+
return this;
202+
}
203+
204+
public Date getValidAfter() {
205+
return validAfter;
206+
}
207+
208+
public Builder<T> validAfter(Date validAfter) {
209+
this.validAfter = validAfter;
210+
return this;
211+
}
212+
213+
public Date getValidBefore() {
214+
return validBefore;
215+
}
216+
217+
public Builder<T> validBefore(Date validBefore) {
218+
this.validBefore = validBefore;
219+
return this;
220+
}
221+
222+
public Map<String, String> getCritOptions() {
223+
return critOptions;
224+
}
225+
226+
public Builder<T> critOptions(Map<String, String> critOptions) {
227+
this.critOptions = critOptions;
228+
return this;
229+
}
230+
231+
public Map<String, String> getExtensions() {
232+
return extensions;
233+
}
234+
235+
public Builder<T> extensions(Map<String, String> extensions) {
236+
this.extensions = extensions;
237+
return this;
238+
}
239+
240+
public byte[] getSignatureKey() {
241+
return signatureKey;
242+
}
243+
244+
public Builder<T> signatureKey(byte[] signatureKey) {
245+
this.signatureKey = signatureKey;
246+
return this;
247+
}
248+
249+
public byte[] getSignature() {
250+
return signature;
251+
}
252+
253+
public Builder<T> signature(byte[] signature) {
254+
this.signature = signature;
255+
return this;
256+
}
257+
}
258+
}

src/main/java/net/schmizz/sshj/common/Buffer.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public PlainBuffer(int size) {
5757
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
5858
public static final int MAX_SIZE = (1 << 30);
5959

60+
/** Maximum size of a uint64 */
61+
private static final BigInteger MAX_UINT64_VALUE = BigInteger.ONE
62+
.shiftLeft(64)
63+
.subtract(BigInteger.ONE);
64+
6065
protected static int getNextPowerOf2(int i) {
6166
int j = 1;
6267
while (j < i) {
@@ -343,10 +348,29 @@ public long readUInt64()
343348
return uint64;
344349
}
345350

346-
@SuppressWarnings("unchecked")
351+
public BigInteger readUInt64AsBigInteger()
352+
throws BufferException {
353+
byte[] magnitude = new byte[8];
354+
readRawBytes(magnitude);
355+
return new BigInteger(1, magnitude);
356+
}
357+
347358
public T putUInt64(long uint64) {
348359
if (uint64 < 0)
349360
throw new IllegalArgumentException("Invalid value: " + uint64);
361+
return putUInt64Unchecked(uint64);
362+
}
363+
364+
public T putUInt64(BigInteger uint64) {
365+
if (uint64.compareTo(MAX_UINT64_VALUE) > 0 ||
366+
uint64.compareTo(BigInteger.ZERO) < 0) {
367+
throw new IllegalArgumentException("Invalid value: " + uint64);
368+
}
369+
return putUInt64Unchecked(uint64.longValue());
370+
}
371+
372+
@SuppressWarnings("unchecked")
373+
private T putUInt64Unchecked(long uint64) {
350374
data[wpos++] = (byte) (uint64 >> 56);
351375
data[wpos++] = (byte) (uint64 >> 48);
352376
data[wpos++] = (byte) (uint64 >> 40);

0 commit comments

Comments
 (0)