使用Amazon AWS Cognito`.well-known / jwks.json`数据无法base64解码一些字段

使用Amazon AWS Cognito 联合身份验证时 ,请在以下位置解析数据:
https://cognito-identity.amazonaws.com/.well-known/jwks_uri这看起来像:

 {"keys":[ {"kty":"RSA", "alg":"RS512", "use":"sig", "kid":"ap-northeast-11", "n":"AI7mc1assO5n6yB4b7jPCFgVLYPSnwt4qp2BhJVAmlXRntRZ5w4910oKNZDOr4fe/BWOI2Z7upUTE/ICXdqirEkjiPbBN/duVy5YcHsQ5+GrxQ/UbytNVN/NsFhdG8W31lsE4dnrGds5cSshLaohyU/aChgaIMbmtU0NSWQ+jwrW8q1PTvnThVQbpte59a0dAwLeOCfrx6kVvs0Y7fX7NXBbFxe8yL+JR3SMJvxBFuYC+/om5EIRIlRexjWpNu7gJnaFFwbxCBNwFHahcg5gdtSkCHJy8Gj78rsgrkEbgoHk29pk8jUzo/O/GuSDGw8qXb6w0R1+UsXPYACOXM8C8+E=", "e":"AQAB"}, ... } 

这工作正确解码n字段使用此代码( Kotlin调用JDK 8 Base64类 ):

 Base64.getDecoder().decode(encodedN.toByteArray()) 

但是当使用具有以下形式的URL的数据的Cognito 用户池时:
https://cognito-idp.${REGION}.amazonaws.com/${POOLID}/.well-known/jwks.json

它具有相同类型的数据,但不会解码。 相反,我最终会遇到以下错误:

非法base64字符5f

由于这是一个下划线_Base64 URL字母表,我试着改变我的解码为:

 Base64.getUrlDecoder().decode(encodedN.toByteArray()) 

但是,第一组数据不能正确解码,因为它包含Base64 URL编码的/和其他无效字符。

有没有一种方法可以处理这两个jwks数据集同一个解码器?!?

注意: 这个问题是由作者故意编写和回答的( 自我回答问题 ),所以在SO中共享有趣问题的解决方案。

问题是亚马逊AWS Cognito团队使用两个不同的Base64编码字母来实现基本相同的功能。 所以你需要检测使用哪一个。

如果编码的字符串以=或者包含+/结尾,那么它就是正常的Base64.getDecoder() 。 如果它包含-_那么它肯定是Base64.getUrlDecoder() 。 否则没有什么特别的,最好使用Base64.getUrlDecoder()因为你不知道长度是否需要填充。

这意味着( 在Kotlin中,但在逻辑上适用于任何语言 ):

 fun base64SafeDecoder(encoded: String): ByteArray { val decoder = if (encoded.endsWith('=') || encoded.any { it == '+' || it == '/' }) { Base64.getDecoder() } else { Base64.getUrlDecoder() } return decoder.decode(encoded.toByteArray()) } 

这对于任何具有Base64解码的语言来说都是个问题,因为他们可能会松散并忽略无效字符(有些是这样),或者它们可能是严格的,并且会抛出异常。 一些用于Base64编码/解码的测试网站也展示了这两种行为,并且对无效字符的无声忽视是危险的。 稍后您将使用解码的结果发生错误。

您可以尝试使用Base64解码的apache变体(org.apache.commons.codec.binary.Base64)。

decodeBase64(String base64String)方法无缝处理base64和base64的url安全编码。 而isBase64方法提供了一个检查来检测一个字符串是否在base64或base64 url​​安全编码。