@@ -44,10 +44,7 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
44
44
if (publicKey == null ) {
45
45
throw new IllegalStateException ("The given Public Key is null." );
46
46
}
47
- if (!isDERSignature (signatureBytes )) {
48
- signatureBytes = JOSEToDER (signatureBytes );
49
- }
50
- boolean valid = crypto .verifySignatureFor (getDescription (), publicKey , contentBytes , signatureBytes );
47
+ boolean valid = crypto .verifySignatureFor (getDescription (), publicKey , contentBytes , JOSEToDER (signatureBytes ));
51
48
52
49
if (!valid ) {
53
50
throw new SignatureVerificationException (this );
@@ -64,7 +61,8 @@ public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
64
61
if (privateKey == null ) {
65
62
throw new IllegalStateException ("The given Private Key is null." );
66
63
}
67
- return crypto .createSignatureFor (getDescription (), privateKey , contentBytes );
64
+ byte [] signature = crypto .createSignatureFor (getDescription (), privateKey , contentBytes );
65
+ return DERToJOSE (signature );
68
66
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e ) {
69
67
throw new SignatureGenerationException (this , e );
70
68
}
@@ -75,15 +73,60 @@ public String getSigningKeyId() {
75
73
return keyProvider .getPrivateKeyId ();
76
74
}
77
75
78
- private boolean isDERSignature (byte [] signature ) {
76
+ //Visible for testing
77
+ byte [] DERToJOSE (byte [] derSignature ) throws SignatureException {
79
78
// DER Structure: http://crypto.stackexchange.com/a/1797
80
- // Should begin with 0x30 and have exactly the expected length
81
- return signature [0 ] == 0x30 && signature .length != ecNumberSize * 2 ;
79
+ boolean derEncoded = derSignature [0 ] == 0x30 && derSignature .length != ecNumberSize * 2 ;
80
+ if (!derEncoded ) {
81
+ throw new SignatureException ("Invalid DER signature format." );
82
+ }
83
+
84
+ final byte [] joseSignature = new byte [ecNumberSize * 2 ];
85
+
86
+ //Skip 0x30
87
+ int offset = 1 ;
88
+ if (derSignature [1 ] == (byte ) 0x81 ) {
89
+ //Skip sign
90
+ offset ++;
91
+ }
92
+
93
+ //Convert to unsigned. Should match DER length - offset
94
+ int encodedLength = derSignature [offset ++] & 0xff ;
95
+ if (encodedLength != derSignature .length - offset ) {
96
+ throw new SignatureException ("Invalid DER signature format." );
97
+ }
98
+
99
+ //Skip 0x02
100
+ offset ++;
101
+
102
+ //Obtain R number length (Includes padding) and skip it
103
+ int rLength = derSignature [offset ++];
104
+ if (rLength > ecNumberSize + 1 ) {
105
+ throw new SignatureException ("Invalid DER signature format." );
106
+ }
107
+ int rPadding = ecNumberSize - rLength ;
108
+ //Retrieve R number
109
+ System .arraycopy (derSignature , offset + Math .max (-rPadding , 0 ), joseSignature , Math .max (rPadding , 0 ), rLength + Math .min (rPadding , 0 ));
110
+
111
+ //Skip R number and 0x02
112
+ offset += rLength + 1 ;
113
+
114
+ //Obtain S number length. (Includes padding)
115
+ int sLength = derSignature [offset ++];
116
+ if (sLength > ecNumberSize + 1 ) {
117
+ throw new SignatureException ("Invalid DER signature format." );
118
+ }
119
+ int sPadding = ecNumberSize - sLength ;
120
+ //Retrieve R number
121
+ System .arraycopy (derSignature , offset + Math .max (-sPadding , 0 ), joseSignature , ecNumberSize + Math .max (sPadding , 0 ), sLength + Math .min (sPadding , 0 ));
122
+
123
+ return joseSignature ;
82
124
}
83
125
84
- private byte [] JOSEToDER (byte [] joseSignature ) throws SignatureException {
126
+ //Visible for testing
127
+ byte [] JOSEToDER (byte [] joseSignature ) throws SignatureException {
85
128
if (joseSignature .length != ecNumberSize * 2 ) {
86
- throw new SignatureException (String . format ( "The signature length was invalid. Expected %d bytes but received %d" , ecNumberSize * 2 , joseSignature . length ) );
129
+ throw new SignatureException ("Invalid JOSE signature format." );
87
130
}
88
131
89
132
// Retrieve R and S number's length and padding.
@@ -94,10 +137,10 @@ private byte[] JOSEToDER(byte[] joseSignature) throws SignatureException {
94
137
95
138
int length = 2 + rLength + 2 + sLength ;
96
139
if (length > 255 ) {
97
- throw new SignatureException ("Invalid ECDSA signature format" );
140
+ throw new SignatureException ("Invalid JOSE signature format. " );
98
141
}
99
142
100
- byte [] derSignature ;
143
+ final byte [] derSignature ;
101
144
int offset ;
102
145
if (length > 0x7f ) {
103
146
derSignature = new byte [3 + length ];
@@ -109,22 +152,38 @@ private byte[] JOSEToDER(byte[] joseSignature) throws SignatureException {
109
152
}
110
153
111
154
// DER Structure: http://crypto.stackexchange.com/a/1797
112
- // Header with length info
155
+ // Header with signature length info
113
156
derSignature [0 ] = (byte ) 0x30 ;
114
- derSignature [offset ++] = (byte ) length ;
157
+ derSignature [offset ++] = (byte ) (length & 0xff );
158
+
159
+ // Header with "min R" number length
115
160
derSignature [offset ++] = (byte ) 0x02 ;
116
161
derSignature [offset ++] = (byte ) rLength ;
117
162
118
163
// R number
119
- System .arraycopy (joseSignature , 0 , derSignature , offset + (rLength - ecNumberSize ), ecNumberSize );
120
- offset += rLength ;
164
+ if (rPadding < 0 ) {
165
+ //Sign
166
+ derSignature [offset ++] = (byte ) 0x00 ;
167
+ System .arraycopy (joseSignature , 0 , derSignature , offset , ecNumberSize );
168
+ offset += ecNumberSize ;
169
+ } else {
170
+ int copyLength = Math .min (ecNumberSize , rLength );
171
+ System .arraycopy (joseSignature , rPadding , derSignature , offset , copyLength );
172
+ offset += copyLength ;
173
+ }
121
174
122
- // S number length
175
+ // Header with "min S" number length
123
176
derSignature [offset ++] = (byte ) 0x02 ;
124
177
derSignature [offset ++] = (byte ) sLength ;
125
178
126
179
// S number
127
- System .arraycopy (joseSignature , ecNumberSize , derSignature , offset + (sLength - ecNumberSize ), ecNumberSize );
180
+ if (sPadding < 0 ) {
181
+ //Sign
182
+ derSignature [offset ++] = (byte ) 0x00 ;
183
+ System .arraycopy (joseSignature , ecNumberSize , derSignature , offset , ecNumberSize );
184
+ } else {
185
+ System .arraycopy (joseSignature , ecNumberSize + sPadding , derSignature , offset , Math .min (ecNumberSize , sLength ));
186
+ }
128
187
129
188
return derSignature ;
130
189
}
@@ -134,7 +193,7 @@ private int countPadding(byte[] bytes, int fromIndex, int toIndex) {
134
193
while (fromIndex + padding < toIndex && bytes [fromIndex + padding ] == 0 ) {
135
194
padding ++;
136
195
}
137
- return bytes [fromIndex + padding ] > 0x7f ? padding : padding - 1 ;
196
+ return ( bytes [fromIndex + padding ] & 0xff ) > 0x7f ? padding - 1 : padding ;
138
197
}
139
198
140
199
//Visible for testing
0 commit comments